temp linear history mode
This commit is contained in:
@@ -612,6 +612,7 @@ export default class ComfyApp {
|
|||||||
node.onExecuted(output);
|
node.onExecuted(output);
|
||||||
}
|
}
|
||||||
workflow.journey.onExecuted(promptID, nodeID, output, queueEntry);
|
workflow.journey.onExecuted(promptID, nodeID, output, queueEntry);
|
||||||
|
workflow.journey.set(get(workflow.journey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1032,11 +1033,14 @@ export default class ComfyApp {
|
|||||||
|
|
||||||
let journeyNode: JourneyNode | null;
|
let journeyNode: JourneyNode | null;
|
||||||
|
|
||||||
if (get(uiState).autoPushJourney) {
|
if (get(uiState).saveHistory) {
|
||||||
const activeNode = targetWorkflow.journey.getActiveNode();
|
const activeNode = targetWorkflow.journey.getActiveNode();
|
||||||
if (activeNode != null) {
|
journeyNode = targetWorkflow.journey.pushPatchOntoActive(targetWorkflow, activeNode);
|
||||||
journeyNode = targetWorkflow.journey.pushPatchOntoActive(targetWorkflow, activeNode);
|
|
||||||
}
|
// if no patch was applied, use currently selected node for prompt image
|
||||||
|
// output purposes
|
||||||
|
if (journeyNode == null)
|
||||||
|
journeyNode = activeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processingQueue = true;
|
this.processingQueue = true;
|
||||||
|
|||||||
@@ -3,39 +3,71 @@
|
|||||||
tree-like graph. It lets you save incremental changes to your workflow and
|
tree-like graph. It lets you save incremental changes to your workflow and
|
||||||
jump between past and present sets of parameters.
|
jump between past and present sets of parameters.
|
||||||
-->
|
-->
|
||||||
|
<script context="module" lang="ts">
|
||||||
|
export type JourneyMode = "linear" | "tree";
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type ComfyApp from './ComfyApp';
|
import type ComfyApp from './ComfyApp';
|
||||||
import type { ComfyBoxWorkflow } from '$lib/stores/workflowState';
|
import type { ComfyBoxWorkflow } from '$lib/stores/workflowState';
|
||||||
import workflowState from '$lib/stores/workflowState';
|
import workflowState from '$lib/stores/workflowState';
|
||||||
import uiState from '$lib/stores/uiState';
|
import uiState from '$lib/stores/uiState';
|
||||||
import { calculateWorkflowParamsPatch, resolvePatch, type JourneyPatchNode, type WritableJourneyStateStore, diffParams } from '$lib/stores/journeyStates';
|
import { calculateWorkflowParamsPatch, resolvePatch, type JourneyPatchNode, type WritableJourneyStateStore, diffParams, JourneyNode } from '$lib/stores/journeyStates';
|
||||||
import JourneyRenderer from './JourneyRenderer.svelte';
|
import JourneyRenderer, { type JourneyNodeEvent } from './JourneyRenderer.svelte';
|
||||||
import { Plus } from "svelte-bootstrap-icons";
|
import { Trash, ClockHistory, Diagram3 } from "svelte-bootstrap-icons";
|
||||||
import { getWorkflowRestoreParams, getWorkflowRestoreParamsFromWorkflow } from '$lib/restoreParameters';
|
import { getWorkflowRestoreParams, getWorkflowRestoreParamsFromWorkflow } from '$lib/restoreParameters';
|
||||||
import notify from '$lib/notify';
|
import notify from '$lib/notify';
|
||||||
import selectionState from '$lib/stores/selectionState';
|
import selectionState from '$lib/stores/selectionState';
|
||||||
import { Checkbox } from '@gradio/form';
|
import { Checkbox } from '@gradio/form';
|
||||||
|
import modalState from '$lib/stores/modalState';
|
||||||
|
import queueState from '$lib/stores/queueState';
|
||||||
|
import PromptDisplay from "$lib/components/PromptDisplay.svelte"
|
||||||
|
import { getQueueEntryImages } from '$lib/stores/uiQueueState';
|
||||||
|
import { SvelteComponent } from 'svelte';
|
||||||
|
import { capitalize } from '$lib/utils';
|
||||||
|
|
||||||
export let app: ComfyApp;
|
export let app: ComfyApp;
|
||||||
|
|
||||||
let workflow: ComfyBoxWorkflow;
|
let workflow: ComfyBoxWorkflow | null = null;
|
||||||
let journey: WritableJourneyStateStore
|
let journey: WritableJourneyStateStore | null = null;
|
||||||
|
let activeNode: JourneyNode | null = null;
|
||||||
|
let mode: JourneyMode = "linear";
|
||||||
|
|
||||||
|
const MODES: [JourneyMode, typeof SvelteComponent][] = [
|
||||||
|
["linear", ClockHistory],
|
||||||
|
["tree", Diagram3],
|
||||||
|
]
|
||||||
|
|
||||||
$: workflow = $workflowState.activeWorkflow
|
$: workflow = $workflowState.activeWorkflow
|
||||||
$: journey = workflow?.journey
|
$: {
|
||||||
|
journey = workflow?.journey
|
||||||
function doAdd() {
|
activeNode = null;
|
||||||
if (!workflow) {
|
updateActiveNode();
|
||||||
notify("No active workflow!", { type: "error" })
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const workflowParams = getWorkflowRestoreParamsFromWorkflow(workflow)
|
|
||||||
const activeNode = journey.getActiveNode();
|
|
||||||
journey.pushPatchOntoActive(workflow, activeNode, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelectNode(e: CustomEvent<{ cyto: cytoscape.Core, node: cytoscape.NodeSingular }>) {
|
function updateActiveNode() {
|
||||||
|
activeNode = journey?.getActiveNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// function doAdd() {
|
||||||
|
// if (!workflow) {
|
||||||
|
// notify("No active workflow!", { type: "error" })
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const activeNode = journey.getActiveNode();
|
||||||
|
// journey.pushPatchOntoActive(workflow, activeNode, true)
|
||||||
|
// }
|
||||||
|
|
||||||
|
function doClearHistory() {
|
||||||
|
if (!confirm("Clear history?"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
journey.clear();
|
||||||
|
notify("History cleared.", { type: "info" })
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelectNode(e: CustomEvent<JourneyNodeEvent>) {
|
||||||
const { node } = e.detail;
|
const { node } = e.detail;
|
||||||
|
|
||||||
const id = node.id();
|
const id = node.id();
|
||||||
@@ -45,6 +77,8 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.debug("[ComfyJourneyView] Journey node", journeyNode)
|
||||||
|
|
||||||
const patch = resolvePatch(journeyNode);
|
const patch = resolvePatch(journeyNode);
|
||||||
|
|
||||||
// ensure reactive state is updated
|
// ensure reactive state is updated
|
||||||
@@ -52,7 +86,40 @@
|
|||||||
$workflowState = $workflowState
|
$workflowState = $workflowState
|
||||||
}
|
}
|
||||||
|
|
||||||
function onHoverNode(e: CustomEvent<{ cyto: cytoscape.Core, node: cytoscape.NodeSingular }>) {
|
function onRightClickNode(e: CustomEvent<JourneyNodeEvent>) {
|
||||||
|
const { node } = e.detail;
|
||||||
|
|
||||||
|
const id = node.id();
|
||||||
|
const journeyNode = $journey.nodesByID[id];
|
||||||
|
if (journeyNode == null) {
|
||||||
|
console.error("[ComfyJourneyView] Missing journey node!", id)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promptID = journeyNode.promptID;
|
||||||
|
if (promptID != null) {
|
||||||
|
const queueEntry = queueState.getQueueEntry(journeyNode.promptID)
|
||||||
|
if (queueEntry?.prompt != null) {
|
||||||
|
modalState.pushModal({
|
||||||
|
title: "Prompt Details",
|
||||||
|
svelteComponent: PromptDisplay,
|
||||||
|
svelteProps: {
|
||||||
|
prompt: queueEntry.prompt,
|
||||||
|
workflow: queueEntry.extraData?.extra_pnginfo?.comfyBoxWorkflow,
|
||||||
|
images: getQueueEntryImages(queueEntry),
|
||||||
|
closeModal: () => modalState.closeAllModals(),
|
||||||
|
expandAll: false,
|
||||||
|
app
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
notify("This journey entry has no prompts yet.", { type: "warning" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHoverNode(e: CustomEvent<JourneyNodeEvent>) {
|
||||||
const { node } = e.detail;
|
const { node } = e.detail;
|
||||||
|
|
||||||
const id = node.id();
|
const id = node.id();
|
||||||
@@ -69,28 +136,43 @@
|
|||||||
$selectionState.currentPatchHoveredNodes = new Set(Object.keys(diff));
|
$selectionState.currentPatchHoveredNodes = new Set(Object.keys(diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onHoverNodeOut(e: CustomEvent<{ cyto: cytoscape.Core, node: cytoscape.NodeSingular }>) {
|
function onHoverNodeOut(e: CustomEvent<JourneyNodeEvent>) {
|
||||||
$selectionState.currentPatchHoveredNodes = new Set();
|
$selectionState.currentPatchHoveredNodes = new Set();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="journey-view">
|
<div class="journey-view">
|
||||||
<JourneyRenderer {workflow} {journey}
|
<div class="top">
|
||||||
on:select_node={onSelectNode}
|
|
||||||
on:hover_node={onHoverNode}
|
|
||||||
on:hover_node_out={onHoverNodeOut}
|
|
||||||
/>
|
|
||||||
<div class="bottom" style:border-top="1px solid var(--panel-border-color)">
|
|
||||||
<Checkbox label="Auto-Push" disabled={$journey.root == null} bind:value={$uiState.autoPushJourney}/>
|
|
||||||
</div>
|
|
||||||
<div class="bottom">
|
|
||||||
<button class="mode-button ternary"
|
<button class="mode-button ternary"
|
||||||
title={"Add new"}
|
title={"Add new"}
|
||||||
disabled={$journey.activeNodeID === null && $journey.root !== null}
|
disabled={$journey.root == null}
|
||||||
on:click={doAdd}>
|
on:click={doClearHistory}>
|
||||||
<Plus width="100%" height="100%" />
|
<Trash width="100%" height="100%" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{#key $journey.version}
|
||||||
|
<JourneyRenderer {workflow} {journey} {mode} {activeNode}
|
||||||
|
on:select_node={onSelectNode}
|
||||||
|
on:right_click_node={onRightClickNode}
|
||||||
|
on:hover_node={onHoverNode}
|
||||||
|
on:hover_node_out={onHoverNodeOut}
|
||||||
|
/>
|
||||||
|
{/key}
|
||||||
|
<div class="bottom" style:border-top="1px solid var(--panel-border-color)">
|
||||||
|
<Checkbox label="Save History" bind:value={$uiState.saveHistory}/>
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
{#each MODES as [theMode, icon]}
|
||||||
|
<!-- 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={() => { mode = theMode; }}>
|
||||||
|
<svelte:component this={icon} width="100%" height="100%" />
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -98,7 +180,12 @@
|
|||||||
|
|
||||||
.journey-view {
|
.journey-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - $button-height * 2);
|
height: calc(100% - $button-height * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
height: $button-height;
|
||||||
|
color: var(--comfy-accent-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
@@ -107,20 +194,20 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
color: var(--comfy-accent-soft);
|
color: var(--comfy-accent-soft);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.mode-button {
|
.mode-button {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
|
||||||
@include square-button;
|
@include square-button;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--body-text-color);
|
color: var(--body-text-color);
|
||||||
}
|
}
|
||||||
&.selected {
|
&.selected {
|
||||||
background-color: var(--panel-background-fill);
|
background-color: var(--panel-background-fill);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
function switchMode(newMode: ComfyPaneMode) {
|
function switchMode(newMode: ComfyPaneMode) {
|
||||||
console.warn("switch", mode, newMode)
|
|
||||||
mode = newMode;
|
mode = newMode;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,67 +1,115 @@
|
|||||||
|
<script context="module" lang="ts">
|
||||||
|
export type JourneyNodeEvent = {
|
||||||
|
cyto: cytoscape.Core,
|
||||||
|
node: cytoscape.NodeSingular
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { JourneyPatchNode, WritableJourneyStateStore } from '$lib/stores/journeyStates';
|
import type { JourneyNode, JourneyPatchNode, WritableJourneyStateStore } from '$lib/stores/journeyStates';
|
||||||
import { ComfyBoxWorkflow } from '$lib/stores/workflowState';
|
import { ComfyBoxWorkflow } from '$lib/stores/workflowState';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import Graph from './graph/Graph.svelte'
|
import Graph from './graph/Graph.svelte'
|
||||||
import type { NodeDataDefinition, EdgeDataDefinition } from 'cytoscape';
|
import type { NodeDataDefinition, EdgeDataDefinition } from 'cytoscape';
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import selectionState from '$lib/stores/selectionState';
|
import selectionState from '$lib/stores/selectionState';
|
||||||
import uiQueueState from '$lib/stores/uiQueueState';
|
import uiQueueState, { getQueueEntryImages } from '$lib/stores/uiQueueState';
|
||||||
|
import queueState from '$lib/stores/queueState';
|
||||||
|
import { convertComfyOutputToComfyURL, countNewLines } from '$lib/utils';
|
||||||
|
import type { ElementDefinition } from 'cytoscape';
|
||||||
|
import type { JourneyMode } from './ComfyJourneyView.svelte';
|
||||||
|
|
||||||
export let workflow: ComfyBoxWorkflow | null = null
|
export let workflow: ComfyBoxWorkflow | null = null
|
||||||
export let journey: WritableJourneyStateStore | null = null
|
export let journey: WritableJourneyStateStore | null = null
|
||||||
|
export let mode: JourneyMode = "linear";
|
||||||
|
export let activeNode: JourneyNode | null = null;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
select_node: { cyto: cytoscape.Core, node: cytoscape.NodeSingular };
|
select_node: JourneyNodeEvent;
|
||||||
hover_node: { cyto: cytoscape.Core, node: cytoscape.NodeSingular };
|
right_click_node: JourneyNodeEvent;
|
||||||
hover_node_out: { cyto: cytoscape.Core, node: cytoscape.NodeSingular };
|
hover_node: JourneyNodeEvent;
|
||||||
|
hover_node_out: JourneyNodeEvent;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
let lastSelected = null;
|
let lastSelected = null;
|
||||||
|
let lastMode = null;
|
||||||
let lastVersion = -1;
|
let lastVersion = -1;
|
||||||
|
|
||||||
let nodes = []
|
let nodes = []
|
||||||
let edges = []
|
let edges = []
|
||||||
$: if ($journey.version !== lastVersion){
|
$: if ($journey.version !== lastVersion || lastMode !== mode){
|
||||||
[nodes, edges] = buildGraph(journey)
|
[nodes, edges] = buildGraph(journey)
|
||||||
lastVersion = $journey.version
|
lastVersion = $journey.version
|
||||||
|
lastMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildGraph(journey: WritableJourneyStateStore | null): [NodeDataDefinition[], EdgeDataDefinition[]] {
|
/*
|
||||||
|
* Converts the journey tree into the renderable graph format Cytoscape expects
|
||||||
|
*/
|
||||||
|
function buildGraph(journey: WritableJourneyStateStore | null): [ElementDefinition[], ElementDefinition[]] {
|
||||||
if (!journey) {
|
if (!journey) {
|
||||||
return [[], []]
|
return [[], []]
|
||||||
}
|
}
|
||||||
|
|
||||||
const journeyState = get(journey);
|
const journeyState = get(journey);
|
||||||
lastSelected = journeyState.activeNodeID;
|
|
||||||
|
|
||||||
const nodes: NodeDataDefinition[] = []
|
const nodes: ElementDefinition[] = []
|
||||||
const edges: EdgeDataDefinition[] = []
|
const edges: ElementDefinition[] = []
|
||||||
|
|
||||||
for (const node of journey.iterateBreadthFirst()) {
|
for (const node of journey.iterateBreadthFirst()) {
|
||||||
if (node.type === "root") {
|
if (node.type === "root") {
|
||||||
nodes.push({
|
nodes.push({
|
||||||
id: node.id,
|
data: {
|
||||||
label: "Start",
|
id: node.id,
|
||||||
selected: node.id === journeyState.activeNodeID,
|
label: "Start",
|
||||||
locked: true
|
},
|
||||||
|
selected: node.id === activeNode?.id,
|
||||||
|
classes: "historyNode"
|
||||||
})
|
})
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const patchNode = node as JourneyPatchNode;
|
const patchNode = node as JourneyPatchNode;
|
||||||
nodes.push({
|
nodes.push({
|
||||||
id: patchNode.id,
|
data: {
|
||||||
label: "P",
|
id: patchNode.id,
|
||||||
selected: node.id === journeyState.activeNodeID,
|
label: "P",
|
||||||
locked: true
|
},
|
||||||
|
selected: node.id === activeNode?.id,
|
||||||
|
classes: "historyNode"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Display a small node between with the patch details
|
||||||
|
const midNodeID = `${patchNode.id}_patch`;
|
||||||
|
|
||||||
|
const patchText = "cfg: 8 -> 10\nsteps: 20->30\na";
|
||||||
|
const patchNodeHeight = countNewLines(patchText) * 11 + 22;
|
||||||
|
|
||||||
|
nodes.push({
|
||||||
|
data: {
|
||||||
|
id: midNodeID,
|
||||||
|
label: patchText,
|
||||||
|
patchNodeHeight
|
||||||
|
},
|
||||||
|
selectable: false,
|
||||||
|
classes: "patchNode"
|
||||||
|
})
|
||||||
|
|
||||||
edges.push({
|
edges.push({
|
||||||
id: `${patchNode.parent.id}_${patchNode.id}`,
|
data: {
|
||||||
source: patchNode.parent.id,
|
id: `${patchNode.parent.id}_${midNodeID}`,
|
||||||
target: patchNode.id,
|
source: patchNode.parent.id,
|
||||||
|
target: midNodeID,
|
||||||
|
},
|
||||||
|
selectable: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
edges.push({
|
||||||
|
data: {
|
||||||
|
id: `${midNodeID}_${patchNode.id}`,
|
||||||
|
source: midNodeID,
|
||||||
|
target: patchNode.id,
|
||||||
|
},
|
||||||
selectable: false,
|
selectable: false,
|
||||||
locked: true
|
locked: true
|
||||||
})
|
})
|
||||||
@@ -88,6 +136,11 @@
|
|||||||
dispatch("select_node", { cyto: e.cy, node })
|
dispatch("select_node", { cyto: e.cy, node })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onNodeRightClicked(e: cytoscape.InputEventObject) {
|
||||||
|
const node = e.target as cytoscape.NodeSingular;
|
||||||
|
dispatch("right_click_node", { cyto: e.cy, node })
|
||||||
|
}
|
||||||
|
|
||||||
function onNodeHovered(e: cytoscape.InputEventObject) {
|
function onNodeHovered(e: cytoscape.InputEventObject) {
|
||||||
const node = e.target as cytoscape.NodeSingular;
|
const node = e.target as cytoscape.NodeSingular;
|
||||||
dispatch("hover_node", { cyto: e.cy, node })
|
dispatch("hover_node", { cyto: e.cy, node })
|
||||||
@@ -104,8 +157,6 @@
|
|||||||
for (const node of cyto.nodes().components()) {
|
for (const node of cyto.nodes().components()) {
|
||||||
const nodeID = node.id()
|
const nodeID = node.id()
|
||||||
if (nodeID === lastSelected) {
|
if (nodeID === lastSelected) {
|
||||||
// why doesn't passing `selected` work in the ctor?
|
|
||||||
node.select();
|
|
||||||
cyto.zoom(1.25);
|
cyto.zoom(1.25);
|
||||||
cyto.center(node)
|
cyto.center(node)
|
||||||
}
|
}
|
||||||
@@ -113,9 +164,14 @@
|
|||||||
const journeyNode = $journey.nodesByID[nodeID]
|
const journeyNode = $journey.nodesByID[nodeID]
|
||||||
if (journeyNode) {
|
if (journeyNode) {
|
||||||
if (journeyNode.promptID != null) {
|
if (journeyNode.promptID != null) {
|
||||||
const queueEntry = $uiQueueState.historyUIEntries.find(e => e.entry.promptID === journeyNode.promptID)
|
const queueEntry = queueState.getQueueEntry(journeyNode.promptID)
|
||||||
if (queueEntry != null && queueEntry.images) {
|
if (queueEntry) {
|
||||||
node.data("bgImage", queueEntry.images[0]);
|
const outputs = getQueueEntryImages(queueEntry);
|
||||||
|
|
||||||
|
if (outputs) {
|
||||||
|
node.data("bgImage", outputs[0]);
|
||||||
|
}
|
||||||
|
console.warn("node.classes", node.classes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,8 +182,9 @@
|
|||||||
cyto.nodes()
|
cyto.nodes()
|
||||||
.lock()
|
.lock()
|
||||||
.on("select", onNodeSelected)
|
.on("select", onNodeSelected)
|
||||||
.on("mouseover", onNodeHovered)
|
.on("cxttapend ", onNodeRightClicked)
|
||||||
.on("mouseout", onNodeHoveredOut)
|
.on("mouseout", onNodeHoveredOut)
|
||||||
|
.on("mouseover", onNodeHovered)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,11 @@
|
|||||||
on:close={close}
|
on:close={close}
|
||||||
on:cancel={doClose}
|
on:cancel={doClose}
|
||||||
on:click|self={close}
|
on:click|self={close}
|
||||||
|
on:contextmenu|preventDefault|stopPropagation
|
||||||
>
|
>
|
||||||
<div on:click|stopPropagation>
|
<div on:click|stopPropagation
|
||||||
|
on:contextmenu|stopPropagation
|
||||||
|
>
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
<slot {closeDialog} />
|
<slot {closeDialog} />
|
||||||
<div class="button-row">
|
<div class="button-row">
|
||||||
|
|||||||
@@ -10,19 +10,22 @@
|
|||||||
import type { Styles } from "@gradio/utils";
|
import type { Styles } from "@gradio/utils";
|
||||||
import { comfyFileToComfyBoxMetadata, comfyURLToComfyFile, countNewLines } from "$lib/utils";
|
import { comfyFileToComfyBoxMetadata, comfyURLToComfyFile, countNewLines } from "$lib/utils";
|
||||||
import ReceiveOutputTargets from "./modal/ReceiveOutputTargets.svelte";
|
import ReceiveOutputTargets from "./modal/ReceiveOutputTargets.svelte";
|
||||||
|
import RestoreParamsTable from "./modal/RestoreParamsTable.svelte";
|
||||||
import workflowState, { type ComfyBoxWorkflow, type WorkflowReceiveOutputTargets } from "$lib/stores/workflowState";
|
import workflowState, { type ComfyBoxWorkflow, type WorkflowReceiveOutputTargets } from "$lib/stores/workflowState";
|
||||||
import type { ComfyReceiveOutputNode } from "$lib/nodes/actions";
|
import type { ComfyReceiveOutputNode } from "$lib/nodes/actions";
|
||||||
import type ComfyApp from "./ComfyApp";
|
import type ComfyApp from "./ComfyApp";
|
||||||
import { TabItem, Tabs } from "@gradio/tabs";
|
import { TabItem, Tabs } from "@gradio/tabs";
|
||||||
import { type ComfyBoxStdPrompt } from "$lib/ComfyBoxStdPrompt";
|
import { type ComfyBoxStdPrompt } from "$lib/ComfyBoxStdPrompt";
|
||||||
import ComfyBoxStdPromptSerializer from "$lib/ComfyBoxStdPromptSerializer";
|
import ComfyBoxStdPromptSerializer from "$lib/ComfyBoxStdPromptSerializer";
|
||||||
import JsonView from "./JsonView.svelte";
|
import JsonView from "./JsonView.svelte";
|
||||||
import type { ZodError } from "zod";
|
import type { ZodError } from "zod";
|
||||||
|
import { concatRestoreParams, getWorkflowRestoreParams, type RestoreParamTargets, type RestoreParamWorkflowNodeTargets } from "$lib/restoreParameters";
|
||||||
|
|
||||||
const splitLength = 50;
|
const splitLength = 50;
|
||||||
|
|
||||||
export let prompt: SerializedPromptInputsAll;
|
export let prompt: SerializedPromptInputsAll;
|
||||||
export let workflow: SerializedAppState | null;
|
export let workflow: SerializedAppState | null;
|
||||||
|
export let restoreParams: RestoreParamTargets = {}
|
||||||
export let images: string[] = []; // list of image URLs to ComfyUI's /view? endpoint
|
export let images: string[] = []; // list of image URLs to ComfyUI's /view? endpoint
|
||||||
export let isMobile: boolean = false;
|
export let isMobile: boolean = false;
|
||||||
export let expandAll: boolean = false;
|
export let expandAll: boolean = false;
|
||||||
@@ -33,6 +36,14 @@
|
|||||||
let stdPromptError: ZodError<any> | null;
|
let stdPromptError: ZodError<any> | null;
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
|
restoreParams = {}
|
||||||
|
|
||||||
|
// TODO other sources than serialized workflow
|
||||||
|
if (workflow != null) {
|
||||||
|
const workflowParams = getWorkflowRestoreParams(workflow.workflow)
|
||||||
|
restoreParams = concatRestoreParams(restoreParams, workflowParams);
|
||||||
|
}
|
||||||
|
|
||||||
const [result, orig] = new ComfyBoxStdPromptSerializer().serialize(prompt, workflow);
|
const [result, orig] = new ComfyBoxStdPromptSerializer().serialize(prompt, workflow);
|
||||||
if (result.success === true) {
|
if (result.success === true) {
|
||||||
stdPrompt = result.data;
|
stdPrompt = result.data;
|
||||||
@@ -44,7 +55,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedTab: "restore-parameters" | "send-outputs" | "standard-prompt" | "prompt" = "standard-prompt";
|
type PromptDisplayTabID = "restore-parameters" | "send-outputs" | "standard-prompt" | "prompt"
|
||||||
|
|
||||||
|
let selectedTab: PromptDisplayTabID = "restore-parameters"
|
||||||
|
|
||||||
let selected_image: number | null = null;
|
let selected_image: number | null = null;
|
||||||
|
|
||||||
@@ -146,15 +159,16 @@
|
|||||||
|
|
||||||
closeModal();
|
closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function doRestoreParams(e: CustomEvent) {
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="prompt-display">
|
<div class="prompt-display">
|
||||||
<div class="prompt-and-sends">
|
<div class="prompt-and-sends">
|
||||||
<Tabs bind:selected={selectedTab}>
|
<Tabs bind:selected={selectedTab}>
|
||||||
<TabItem id="restore-parameters" name="Restore Parameters">
|
<TabItem id="restore-parameters" name="Restore Parameters">
|
||||||
<Block>
|
<RestoreParamsTable {restoreParams} on:restore={doRestoreParams} />
|
||||||
<BlockTitle>Parameters</BlockTitle>
|
|
||||||
</Block>
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
{#if comfyBoxImages.length > 0}
|
{#if comfyBoxImages.length > 0}
|
||||||
<TabItem id="send-outputs" name="Send Outputs">
|
<TabItem id="send-outputs" name="Send Outputs">
|
||||||
|
|||||||
@@ -70,40 +70,34 @@
|
|||||||
|
|
||||||
|
|
||||||
{#if container}
|
{#if container}
|
||||||
{#key $attrsChanged}
|
<Container {layoutState} {container} {classes} {zIndex} {showHandles} {isMobile} />
|
||||||
<Container {layoutState} {container} {classes} {zIndex} {showHandles} {isMobile} />
|
|
||||||
{/key}
|
|
||||||
{:else if widget && widget.node}
|
{:else if widget && widget.node}
|
||||||
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets"}
|
{@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets"}
|
||||||
{@const hidden = isHidden(widget)}
|
{@const hidden = isHidden(widget)}
|
||||||
{@const hovered = $uiState.uiUnlocked && $selectionState.currentHovered.has(widget.id)}
|
{@const hovered = $uiState.uiUnlocked && $selectionState.currentHovered.has(widget.id)}
|
||||||
{@const selected = $uiState.uiUnlocked && $selectionState.currentSelection.includes(widget.id)}
|
{@const selected = $uiState.uiUnlocked && $selectionState.currentSelection.includes(widget.id)}
|
||||||
{#key $attrsChanged}
|
<div class="widget {widget.attrs.classes} {getWidgetClass()}"
|
||||||
{#key $propsChanged}
|
class:edit={edit}
|
||||||
<div class="widget {widget.attrs.classes} {getWidgetClass()}"
|
class:hovered
|
||||||
class:edit={edit}
|
class:selected
|
||||||
class:hovered
|
class:patch-affected={$selectionState.currentPatchHoveredNodes.has(widget.node.id)}
|
||||||
class:selected
|
class:is-executing={$queueState.runningNodeID && $queueState.runningNodeID == widget.node.id}
|
||||||
class:patch-affected={$selectionState.currentPatchHoveredNodes.has(widget.node.id)}
|
class:hidden={hidden}
|
||||||
class:is-executing={$queueState.runningNodeID && $queueState.runningNodeID == widget.node.id}
|
>
|
||||||
class:hidden={hidden}
|
<svelte:component this={widget.node.svelteComponentType} {widget} {isMobile} />
|
||||||
>
|
</div>
|
||||||
<svelte:component this={widget.node.svelteComponentType} {widget} {isMobile} />
|
{#if hidden && edit}
|
||||||
</div>
|
<div class="handle handle-hidden" class:hidden={!edit} />
|
||||||
{#if hidden && edit}
|
{/if}
|
||||||
<div class="handle handle-hidden" class:hidden={!edit} />
|
{#if showHandles || hovered}
|
||||||
{/if}
|
<div class="handle handle-widget"
|
||||||
{#if showHandles || hovered}
|
class:hovered
|
||||||
<div class="handle handle-widget"
|
data-drag-item-id={widget.id}
|
||||||
class:hovered
|
on:mousedown={_startDrag}
|
||||||
data-drag-item-id={widget.id}
|
on:touchstart={_startDrag}
|
||||||
on:mousedown={_startDrag}
|
on:mouseup={_stopDrag}
|
||||||
on:touchstart={_startDrag}
|
on:touchend={_stopDrag}/>
|
||||||
on:mouseup={_stopDrag}
|
{/if}
|
||||||
on:touchend={_stopDrag}/>
|
|
||||||
{/if}
|
|
||||||
{/key}
|
|
||||||
{/key}
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -10,12 +10,11 @@
|
|||||||
import cytoscape from "cytoscape"
|
import cytoscape from "cytoscape"
|
||||||
import dagre from "cytoscape-dagre"
|
import dagre from "cytoscape-dagre"
|
||||||
import GraphStyles from "./GraphStyles"
|
import GraphStyles from "./GraphStyles"
|
||||||
import type { EdgeDataDefinition } from "cytoscape";
|
import type { ElementDefinition } from "cytoscape";
|
||||||
import type { NodeDataDefinition } from "cytoscape";
|
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
export let nodes: ReadonlyArray<NodeDataDefinition>;
|
export let nodes: ReadonlyArray<ElementDefinition>;
|
||||||
export let edges: ReadonlyArray<EdgeDataDefinition>;
|
export let edges: ReadonlyArray<ElementDefinition>;
|
||||||
|
|
||||||
export let style: string = ""
|
export let style: string = ""
|
||||||
|
|
||||||
@@ -32,6 +31,7 @@
|
|||||||
|
|
||||||
function rebuildGraph() {
|
function rebuildGraph() {
|
||||||
cytoscape.use(dagre)
|
cytoscape.use(dagre)
|
||||||
|
cytoscape.warnings(false)
|
||||||
|
|
||||||
cyInstance = cytoscape({
|
cyInstance = cytoscape({
|
||||||
container: refElement,
|
container: refElement,
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
wheelSensitivity: 0.1,
|
wheelSensitivity: 0.1,
|
||||||
maxZoom: 3,
|
maxZoom: 3,
|
||||||
minZoom: 0.5,
|
minZoom: 0.5,
|
||||||
|
selectionType: "single"
|
||||||
})
|
})
|
||||||
|
|
||||||
cyInstance.on("add", () => {
|
cyInstance.on("add", () => {
|
||||||
@@ -51,18 +52,25 @@
|
|||||||
.run()
|
.run()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Prevents the unselection of nodes when clicking on the background
|
||||||
|
cyInstance.on('click', (event) => {
|
||||||
|
if (event.target === cyInstance) {
|
||||||
|
// click on the background
|
||||||
|
cyInstance.nodes(".historyNode").unselectify();
|
||||||
|
} else {
|
||||||
|
cyInstance.nodes(".historyNode").selectify();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
cyInstance.add({
|
node.group = "nodes"
|
||||||
group: 'nodes',
|
cyInstance.add(node)
|
||||||
data: { ...node }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const edge of edges) {
|
for (const edge of edges) {
|
||||||
cyInstance.add({
|
edge.group = "edges";
|
||||||
group: 'edges',
|
console.warn(edge)
|
||||||
data: { ...edge }
|
cyInstance.add(edge)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch("rebuilt", { cyto: cyInstance })
|
dispatch("rebuilt", { cyto: cyInstance })
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const styles: Stylesheet[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: "node",
|
selector: ".historyNode",
|
||||||
style: {
|
style: {
|
||||||
"width": "100",
|
"width": "100",
|
||||||
"height": "100",
|
"height": "100",
|
||||||
@@ -35,7 +35,7 @@ const styles: Stylesheet[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: "node[bgImage]",
|
selector: "node.historyNode[bgImage]",
|
||||||
style: {
|
style: {
|
||||||
"label": "",
|
"label": "",
|
||||||
"background-image": "data(bgImage)",
|
"background-image": "data(bgImage)",
|
||||||
@@ -45,13 +45,32 @@ const styles: Stylesheet[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: "node:selected",
|
selector: ".historyNode:selected",
|
||||||
style: {
|
style: {
|
||||||
"background-color": "#f97316",
|
"background-color": "#f97316",
|
||||||
"color": "white",
|
"color": "white",
|
||||||
"border-color": "#ea580c",
|
"border-color": "#ea580c",
|
||||||
"line-color": "#0e76ba",
|
"line-color": "#0e76ba",
|
||||||
"target-arrow-color": "#0e76ba"
|
"target-arrow-color": "#0e76ba",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".patchNode",
|
||||||
|
style: {
|
||||||
|
"width": "100",
|
||||||
|
"height": "data(patchNodeHeight)",
|
||||||
|
"shape": "round-rectangle",
|
||||||
|
"font-family": "Arial",
|
||||||
|
"font-size": "11",
|
||||||
|
"font-weight": "normal",
|
||||||
|
"content": `data(label)`,
|
||||||
|
"text-valign": "center",
|
||||||
|
"text-wrap": "wrap",
|
||||||
|
"text-max-width": "140",
|
||||||
|
"background-color": "#333",
|
||||||
|
"border-color": "#black",
|
||||||
|
"border-width": "1",
|
||||||
|
"color": "white",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
114
src/lib/components/modal/RestoreParamsTable.svelte
Normal file
114
src/lib/components/modal/RestoreParamsTable.svelte
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { ComfyReceiveOutputNode } from "$lib/nodes/actions";
|
||||||
|
import type { ComfyWidgetNode } from "$lib/nodes/widgets";
|
||||||
|
import type { RestoreParamTargets } from "$lib/restoreParameters";
|
||||||
|
import { isComfyWidgetNode } from "$lib/stores/layoutStates";
|
||||||
|
import type { ComfyBoxWorkflow, WorkflowReceiveOutputTargets } from "$lib/stores/workflowState";
|
||||||
|
import workflowState from "$lib/stores/workflowState";
|
||||||
|
import { Block, BlockTitle } from "@gradio/atoms";
|
||||||
|
import { Button } from "@gradio/button";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
type UIRestoreParam = {
|
||||||
|
node: ComfyWidgetNode,
|
||||||
|
widget: WidgetLayout,
|
||||||
|
sources: RestoreParamSource[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher<{
|
||||||
|
restore: {};
|
||||||
|
}>();
|
||||||
|
|
||||||
|
export let restoreParams: RestoreParamTargets = {};
|
||||||
|
let uiRestoreParams: UIRestoreParam[] = []
|
||||||
|
|
||||||
|
$: uiRestoreParams = buildForUI(restoreParams);
|
||||||
|
|
||||||
|
function buildForUI(restoreParams: RestoreParamTargets): UIRestoreParam[] {
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
for (const [nodeID, sources] of Object.entries(restoreParams)) {
|
||||||
|
const node = workflow.graph.getNodeByIdRecursive(nodeID);
|
||||||
|
if (node == null || !isComfyWidgetNode(node))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const widget = node.dragItem;
|
||||||
|
if (widget == null) {
|
||||||
|
console.error("[RestoreParamsTable] Node missing layoutState widget!!!", node)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({ node, widget, sources })
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
let workflow: ComfyBoxWorkflow;
|
||||||
|
$: workflow = workflowState.getActiveWorkflow();
|
||||||
|
|
||||||
|
function doRestore(e: MouseEvent) {
|
||||||
|
dispatch("restore", {})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="scroll-container">
|
||||||
|
{#if workflow == null}
|
||||||
|
<div>No workflow is active.</div>
|
||||||
|
{:else if Object.keys(uiRestoreParams).length === 0}
|
||||||
|
<div>
|
||||||
|
<p>No parameters to restore found in this workflow.</p>
|
||||||
|
<p>(TODO: Only parameters compatible with the currently active workflow can be restored right now)</p>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<Block>
|
||||||
|
<BlockTitle>Parameters</BlockTitle>
|
||||||
|
<Block>
|
||||||
|
<Button variant="primary" on:click={doRestore}>
|
||||||
|
Restore
|
||||||
|
</Button>
|
||||||
|
</Block>
|
||||||
|
{#each uiRestoreParams as { node, widget, sources }}
|
||||||
|
<Block>
|
||||||
|
<BlockTitle>{widget.attrs.title || node.title}</BlockTitle>
|
||||||
|
{#each sources as source}
|
||||||
|
<div class="target">
|
||||||
|
<div class="target-name-and-desc">
|
||||||
|
<div class="target-name">➤ {source.type}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</Block>
|
||||||
|
{/each}
|
||||||
|
</Block>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.scroll-container {
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 0%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
> :global(.block) {
|
||||||
|
background: var(--panel-background-fill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.target {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.target-name-and-desc {
|
||||||
|
margin: auto auto auto 0;
|
||||||
|
left: 0px;
|
||||||
|
|
||||||
|
.target-desc {
|
||||||
|
opacity: 65%;
|
||||||
|
font-size: 11pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -118,7 +118,7 @@ export default class ComfyGraphNode extends LGraphNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get dragItem(): WidgetLayout | null {
|
get dragItem(): WidgetLayout | null {
|
||||||
return layoutStates.getDragItemByNode(this);
|
return layoutStates.getDragItemByNode(this) as WidgetLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
get workflow(): ComfyBoxWorkflow | null {
|
get workflow(): ComfyBoxWorkflow | null {
|
||||||
|
|||||||
@@ -185,24 +185,23 @@ export function getWorkflowRestoreParamsFromWorkflow(workflow: ComfyBoxWorkflow,
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorkflowRestoreParams(workflow: ComfyBoxWorkflow, prompt: SerializedLGraph): RestoreParamWorkflowNodeTargets {
|
export function getWorkflowRestoreParams(serGraph: SerializedLGraph, noExclude: boolean = false): RestoreParamWorkflowNodeTargets {
|
||||||
const result = {}
|
const result = {}
|
||||||
|
|
||||||
const graph = workflow.graph;
|
for (const node of serGraph.nodes) {
|
||||||
|
if (!isSerializedComfyWidgetNode(node))
|
||||||
|
continue;
|
||||||
|
|
||||||
// Find nodes that correspond to *this* workflow exactly, since we can
|
if (!noExclude && node.properties.excludeFromJourney)
|
||||||
// easily match up the nodes between each (their IDs will be the same)
|
continue;
|
||||||
for (const serNode of prompt.nodes) {
|
|
||||||
const foundNode = graph.getNodeByIdRecursive(serNode.id);
|
const finalValue = node.comfyValue
|
||||||
if (isComfyWidgetNode(foundNode) && foundNode.type === serNode.type) {
|
if (finalValue != null) {
|
||||||
const finalValue = (serNode as SerializedComfyWidgetNode).comfyValue;
|
const source: RestoreParamSourceWorkflowNode = {
|
||||||
if (finalValue != null) {
|
type: "workflow",
|
||||||
const source: RestoreParamSourceWorkflowNode = {
|
finalValue,
|
||||||
type: "workflow",
|
|
||||||
finalValue,
|
|
||||||
}
|
|
||||||
result[foundNode.id] = source;
|
|
||||||
}
|
}
|
||||||
|
result[node.id] = source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,10 +249,10 @@ export function getBackendRestoreParams(workflow: ComfyBoxWorkflow, prompt: Seri
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function restoreParameters(workflow: ComfyBoxWorkflow, prompt: SerializedPrompt): RestoreParamTargets {
|
export default function getRestoreParameters(workflow: ComfyBoxWorkflow, prompt: SerializedPrompt): RestoreParamTargets {
|
||||||
const result = {}
|
const result = {}
|
||||||
|
|
||||||
const workflowParams = getWorkflowRestoreParams(workflow, prompt.workflow);
|
const workflowParams = getWorkflowRestoreParams(prompt.workflow);
|
||||||
concatRestoreParams(result, workflowParams);
|
concatRestoreParams(result, workflowParams);
|
||||||
|
|
||||||
const backendParams = getBackendRestoreParams(workflow, prompt);
|
const backendParams = getBackendRestoreParams(workflow, prompt);
|
||||||
|
|||||||
@@ -185,7 +185,8 @@ function create() {
|
|||||||
if (activeNode == null) {
|
if (activeNode == null) {
|
||||||
// add root node
|
// add root node
|
||||||
if (get(store).root != null) {
|
if (get(store).root != null) {
|
||||||
return;
|
console.debug("[journeyStates] Root already exists")
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
journeyNode = addNode(workflowParams, null);
|
journeyNode = addNode(workflowParams, null);
|
||||||
if (showNotification)
|
if (showNotification)
|
||||||
@@ -196,9 +197,10 @@ function create() {
|
|||||||
const patch = calculateWorkflowParamsPatch(activeNode, workflowParams);
|
const patch = calculateWorkflowParamsPatch(activeNode, workflowParams);
|
||||||
const patchedCount = Object.keys(patch).length;
|
const patchedCount = Object.keys(patch).length;
|
||||||
if (patchedCount === 0) {
|
if (patchedCount === 0) {
|
||||||
|
console.debug("[journeyStates] Patch had no diff")
|
||||||
if (showNotification)
|
if (showNotification)
|
||||||
notify("No changes were made to active parameters yet.", { type: "warning" })
|
notify("No changes were made to active parameters yet.", { type: "warning" })
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
journeyNode = addNode(patch, activeNode);
|
journeyNode = addNode(patch, activeNode);
|
||||||
if (showNotification)
|
if (showNotification)
|
||||||
@@ -209,6 +211,7 @@ function create() {
|
|||||||
selectNode(journeyNode);
|
selectNode(journeyNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.debug("[journeyStates] added node", journeyNode)
|
||||||
return journeyNode;
|
return journeyNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +271,11 @@ function create() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
store.update(s => {
|
||||||
|
s.version += 1;
|
||||||
|
s.activeNodeID = journeyNode.id;
|
||||||
|
return s;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ function convertEntry(entry: QueueEntry, status: QueueUIEntryStatus): QueueUIEnt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getQueueEntryImages(queueEntry: QueueEntry): string[] {
|
||||||
|
return Object.values(queueEntry.outputs)
|
||||||
|
.filter(o => o.images)
|
||||||
|
.flatMap(o => o.images)
|
||||||
|
.map(convertComfyOutputToComfyURL);
|
||||||
|
}
|
||||||
|
|
||||||
function convertPendingEntry(entry: QueueEntry, status: QueueUIEntryStatus): QueueUIEntry {
|
function convertPendingEntry(entry: QueueEntry, status: QueueUIEntryStatus): QueueUIEntry {
|
||||||
const result = convertEntry(entry, status);
|
const result = convertEntry(entry, status);
|
||||||
|
|
||||||
@@ -97,10 +104,7 @@ function convertPendingEntry(entry: QueueEntry, status: QueueUIEntryStatus): Que
|
|||||||
result.images = thumbnails.map(convertComfyOutputToComfyURL);
|
result.images = thumbnails.map(convertComfyOutputToComfyURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputs = Object.values(entry.outputs)
|
const outputs = getQueueEntryImages(entry);
|
||||||
.filter(o => o.images)
|
|
||||||
.flatMap(o => o.images)
|
|
||||||
.map(convertComfyOutputToComfyURL);
|
|
||||||
if (outputs) {
|
if (outputs) {
|
||||||
result.images = result.images.concat(outputs)
|
result.images = result.images.concat(outputs)
|
||||||
}
|
}
|
||||||
@@ -111,11 +115,7 @@ function convertPendingEntry(entry: QueueEntry, status: QueueUIEntryStatus): Que
|
|||||||
function convertCompletedEntry(entry: CompletedQueueEntry): QueueUIEntry {
|
function convertCompletedEntry(entry: CompletedQueueEntry): QueueUIEntry {
|
||||||
const result = convertEntry(entry.entry, entry.status);
|
const result = convertEntry(entry.entry, entry.status);
|
||||||
|
|
||||||
const images = Object.values(entry.entry.outputs)
|
result.images = getQueueEntryImages(entry.entry)
|
||||||
.filter(o => o.images)
|
|
||||||
.flatMap(o => o.images)
|
|
||||||
.map(convertComfyOutputToComfyURL);
|
|
||||||
result.images = images
|
|
||||||
|
|
||||||
if (entry.message)
|
if (entry.message)
|
||||||
result.submessage = entry.message
|
result.submessage = entry.message
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export type UIState = {
|
|||||||
|
|
||||||
activeError: PromptID | null
|
activeError: PromptID | null
|
||||||
|
|
||||||
autoPushJourney: boolean
|
saveHistory: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type UIStateOps = {
|
type UIStateOps = {
|
||||||
@@ -38,7 +38,7 @@ const store: Writable<UIState> = writable(
|
|||||||
|
|
||||||
activeError: null,
|
activeError: null,
|
||||||
|
|
||||||
autoPushJourney: true
|
saveHistory: true
|
||||||
})
|
})
|
||||||
|
|
||||||
function reconnecting() {
|
function reconnecting() {
|
||||||
|
|||||||
Reference in New Issue
Block a user