History fixes
This commit is contained in:
@@ -1113,7 +1113,7 @@ export default class ComfyApp {
|
||||
queueState.afterQueued(workflow.id, response.promptID, response.number, p.output, extraData)
|
||||
workflowState.afterQueued(workflow.id, response.promptID)
|
||||
if (journeyNode != null) {
|
||||
journeyNode.promptID = response.promptID;
|
||||
targetWorkflow.journey.afterQueued(journeyNode, response.promptID);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import selectionState from '$lib/stores/selectionState';
|
||||
import { Checkbox } from '@gradio/form';
|
||||
import modalState from '$lib/stores/modalState';
|
||||
import queueState from '$lib/stores/queueState';
|
||||
import queueState, { type QueueEntry } from '$lib/stores/queueState';
|
||||
import PromptDisplay from "$lib/components/PromptDisplay.svelte"
|
||||
import { getQueueEntryImages } from '$lib/stores/uiQueueState';
|
||||
import { SvelteComponent } from 'svelte';
|
||||
@@ -91,23 +91,25 @@
|
||||
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
|
||||
},
|
||||
})
|
||||
}
|
||||
// pick first resolved prompt
|
||||
const queueEntry: QueueEntry | null =
|
||||
Array.from(journeyNode.promptIDs)
|
||||
.map(id => queueState.getQueueEntry(id))
|
||||
.find(qe => qe?.prompt != null);
|
||||
|
||||
if (queueEntry) {
|
||||
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" })
|
||||
|
||||
@@ -50,23 +50,31 @@
|
||||
return a[1].name > b[1].name ? 1 : -1
|
||||
})
|
||||
|
||||
for (const [nodeID, source] of sorted) {
|
||||
const MAX_ENTRIES = 5
|
||||
const entries = sorted.slice(0, MAX_ENTRIES)
|
||||
const leftover = sorted.length - MAX_ENTRIES
|
||||
|
||||
for (const [nodeID, source] of entries) {
|
||||
let line = ""
|
||||
switch (source.nodeType) {
|
||||
case "ui/text":
|
||||
line = `${source.name} (changed)`
|
||||
line = `${source.name}: (changed)`
|
||||
break;
|
||||
default:
|
||||
const prevValue = prev[nodeID];
|
||||
let prevValueStr = "???"
|
||||
if (prevValue)
|
||||
prevValueStr = prevValue.finalValue
|
||||
line = `${source.name}: ${prevValueStr} -> ${source.finalValue}`
|
||||
line = `${source.name}: ${prevValueStr} → ${source.finalValue}`
|
||||
break;
|
||||
}
|
||||
lines.push(line)
|
||||
}
|
||||
|
||||
if (leftover > 0) {
|
||||
lines.push(`(+ ${leftover} more)`)
|
||||
}
|
||||
|
||||
return lines.join("\n")
|
||||
}
|
||||
|
||||
@@ -128,8 +136,6 @@
|
||||
const patchText = makePatchText(patchNode.patch, prev);
|
||||
const patchNodeHeight = countNewLines(patchText) * 11 + 22;
|
||||
|
||||
console.debug("[JourneyRenderer] Patch text", prev, patchText);
|
||||
|
||||
nodes.push({
|
||||
data: {
|
||||
id: midNodeID,
|
||||
@@ -224,8 +230,8 @@
|
||||
|
||||
const journeyNode = $journey.nodesByID[nodeID]
|
||||
if (journeyNode) {
|
||||
if (journeyNode.promptID != null) {
|
||||
const queueEntry = queueState.getQueueEntry(journeyNode.promptID)
|
||||
if (journeyNode.promptIDs) {
|
||||
const queueEntry = Array.from(journeyNode.promptIDs).map(id => queueState.getQueueEntry(id)).find(Boolean);
|
||||
if (queueEntry) {
|
||||
const outputs = getQueueEntryImages(queueEntry);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import Gallery from "$lib/components/gradio/gallery/Gallery.svelte";
|
||||
import { ImageViewer } from "$lib/ImageViewer";
|
||||
import type { Styles } from "@gradio/utils";
|
||||
import { comfyFileToComfyBoxMetadata, comfyURLToComfyFile, countNewLines } from "$lib/utils";
|
||||
import { comfyFileToComfyBoxMetadata, comfyURLToComfyFile, countNewLines, isMultiline } from "$lib/utils";
|
||||
import ReceiveOutputTargets from "./modal/ReceiveOutputTargets.svelte";
|
||||
import RestoreParamsTable from "./modal/RestoreParamsTable.svelte";
|
||||
import workflowState, { type ComfyBoxWorkflow, type WorkflowReceiveOutputTargets } from "$lib/stores/workflowState";
|
||||
@@ -41,6 +41,7 @@
|
||||
// TODO other sources than serialized workflow
|
||||
if (workflow != null) {
|
||||
const workflowParams = getWorkflowRestoreParamsUsingLayout(workflow.workflow, workflow.layout)
|
||||
console.error("GETPARMS", workflowParams)
|
||||
restoreParams = concatRestoreParams(restoreParams, workflowParams);
|
||||
}
|
||||
|
||||
@@ -100,10 +101,6 @@
|
||||
&& typeof input[1] === "number"
|
||||
}
|
||||
|
||||
function isMultiline(input: any): boolean {
|
||||
return typeof input === "string" && (input.length > splitLength || countNewLines(input) > 1);
|
||||
}
|
||||
|
||||
function formatInput(input: any): string {
|
||||
if (typeof input === "string")
|
||||
return input
|
||||
|
||||
@@ -68,8 +68,9 @@ const styles: Stylesheet[] = [
|
||||
"text-valign": "center",
|
||||
"text-wrap": "wrap",
|
||||
"text-max-width": "140",
|
||||
"background-color": "#333",
|
||||
"border-color": "#black",
|
||||
"line-height": "1.5",
|
||||
"background-color": "#374151",
|
||||
"border-color": "#1f2937",
|
||||
"border-width": "1",
|
||||
"color": "white",
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<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 { ComfyWidgetNode } from "$lib/nodes/widgets";
|
||||
import type { RestoreParamSource, RestoreParamTargets } from "$lib/restoreParameters";
|
||||
import { isComfyWidgetNode, type WidgetLayout } from "$lib/stores/layoutStates";
|
||||
import type { ComfyBoxWorkflow, WorkflowReceiveOutputTargets } from "$lib/stores/workflowState";
|
||||
import workflowState 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";
|
||||
import deepEqual from "deep-equal";
|
||||
import { capitalize, countNewLines, isMultiline } from "$lib/utils";
|
||||
import { TextBox } from "@gradio/form";
|
||||
|
||||
type UIRestoreParam = {
|
||||
node: ComfyWidgetNode,
|
||||
@@ -32,14 +35,21 @@
|
||||
if (node == null || !isComfyWidgetNode(node))
|
||||
continue;
|
||||
|
||||
const nodeValue = node.getValue();
|
||||
const foundSources = sources.filter(s => !deepEqual(nodeValue, s.finalValue));
|
||||
if (foundSources.length === 0)
|
||||
continue;
|
||||
|
||||
const widget = node.dragItem;
|
||||
if (widget == null) {
|
||||
console.error("[RestoreParamsTable] Node missing layoutState widget!!!", node)
|
||||
}
|
||||
|
||||
result.push({ node, widget, sources })
|
||||
result.push({ node, widget, sources: foundSources })
|
||||
}
|
||||
|
||||
console.warn("RESTORE PARAMS", restoreParams, "->", result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -48,7 +58,7 @@
|
||||
|
||||
function doRestore(e: MouseEvent) {
|
||||
dispatch("restore", {})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="scroll-container">
|
||||
@@ -57,7 +67,7 @@
|
||||
{: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>
|
||||
<p>(Either prompt is unchanged from active workflow, or the workflow the parameters were saved from was different)</p>
|
||||
</div>
|
||||
{:else}
|
||||
<Block>
|
||||
@@ -69,11 +79,22 @@
|
||||
</Block>
|
||||
{#each uiRestoreParams as { node, widget, sources }}
|
||||
<Block>
|
||||
<BlockTitle>{widget.attrs.title || node.title}</BlockTitle>
|
||||
<div class="target-name">➤ {widget.attrs.title || node.title}</div>
|
||||
{#each sources as source}
|
||||
{@const value = String(source.finalValue)}
|
||||
<div class="target">
|
||||
<div class="target-name-and-desc">
|
||||
<div class="target-name">➤ {source.type}</div>
|
||||
<Block>
|
||||
<BlockTitle>{capitalize(source.type)}</BlockTitle>
|
||||
<div>
|
||||
{#if isMultiline(value, 20)}
|
||||
{@const lines = Math.max(countNewLines(value), value.length / 20)}
|
||||
<TextBox show_label={false} label={''} {value} {lines} max_lines={lines} />
|
||||
{:else}
|
||||
<TextBox show_label={false} label={''} {value} lines={1} max_lines={1} />
|
||||
{/if}
|
||||
</div>
|
||||
</Block>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -95,20 +116,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
.target-name {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.target {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
text-align: left;
|
||||
|
||||
.target-name-and-desc {
|
||||
margin: auto auto auto 0;
|
||||
left: 0px;
|
||||
:global(.block) {
|
||||
background: var(--panel-background-fill);
|
||||
}
|
||||
|
||||
.target-desc {
|
||||
opacity: 65%;
|
||||
font-size: 11pt;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
@include json-view;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { INodeInputSlot, NodeID, SerializedLGraph } from "@litegraph-ts/core";
|
||||
import type { INodeInputSlot, NodeID, SerializedLGraph, SerializedLGraphNode } from "@litegraph-ts/core";
|
||||
import type { SerializedPrompt } from "./components/ComfyApp";
|
||||
import type { ComfyWidgetNode } from "./nodes/widgets";
|
||||
import type { SerializedComfyWidgetNode } from "./nodes/widgets/ComfyWidgetNode";
|
||||
@@ -242,15 +242,29 @@ export function getWorkflowRestoreParams(serGraph: SerializedLGraph, workflow?:
|
||||
return result
|
||||
}
|
||||
|
||||
function* iterateSerializedNodesRecursive(serGraph: SerializedLGraph): Iterable<SerializedLGraphNode> {
|
||||
for (const serNode of serGraph.nodes) {
|
||||
yield serNode;
|
||||
|
||||
if (serNode.type === "graph/subgraph") {
|
||||
for (const childNode of iterateSerializedNodesRecursive((serNode as any).subgraph)) {
|
||||
yield childNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getWorkflowRestoreParamsUsingLayout(serGraph: SerializedLGraph, layout?: SerializedLayoutState, noExclude: boolean = false): RestoreParamWorkflowNodeTargets {
|
||||
const result = {}
|
||||
|
||||
for (const serNode of serGraph.nodes) {
|
||||
if (!isSerializedComfyWidgetNode(serNode))
|
||||
for (const serNode of iterateSerializedNodesRecursive(serGraph)) {
|
||||
if (!isSerializedComfyWidgetNode(serNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!noExclude && serNode.properties.excludeFromJourney)
|
||||
if (!noExclude && serNode.properties.excludeFromJourney) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = null;
|
||||
const serWidget = Array.from(Object.values(layout?.allItems || {})).find(di => di.dragItem.type === "widget" && di.dragItem.nodeId === serNode.id)
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface JourneyNode {
|
||||
id: JourneyNodeID,
|
||||
type: JourneyNodeType,
|
||||
children: JourneyPatchNode[],
|
||||
promptID?: PromptID,
|
||||
promptIDs: Set<PromptID>,
|
||||
images?: string[]
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ export function calculateWorkflowParamsPatch(parent: JourneyNode, newParams: Res
|
||||
export type JourneyState = {
|
||||
root: JourneyRootNode | null,
|
||||
nodesByID: Record<JourneyNodeID, JourneyNode>,
|
||||
nodesByPromptID: Record<PromptID, JourneyNode>,
|
||||
activeNodeID: JourneyNodeID | null,
|
||||
|
||||
/*
|
||||
@@ -117,6 +118,7 @@ type JourneyStateOps = {
|
||||
iterateBreadthFirst: (id?: JourneyNodeID | null) => Iterable<JourneyNode>,
|
||||
iterateLinearPath: (id: JourneyNodeID) => Iterable<JourneyNode>,
|
||||
pushPatchOntoActive: (workflow: ComfyBoxWorkflow, activeNode?: JourneyNode, showNotification?: boolean) => JourneyNode | null
|
||||
afterQueued: (journeyNode: JourneyNode, promptID: PromptID) => void,
|
||||
onExecuted: (promptID: PromptID, nodeID: ComfyNodeID, output: SerializedPromptOutput, queueEntry: QueueEntry) => void
|
||||
}
|
||||
|
||||
@@ -127,6 +129,7 @@ function create() {
|
||||
{
|
||||
root: null,
|
||||
nodesByID: {},
|
||||
nodesByPromptID: {},
|
||||
activeNodeID: null,
|
||||
version: 0
|
||||
})
|
||||
@@ -135,6 +138,7 @@ function create() {
|
||||
store.set({
|
||||
root: null,
|
||||
nodesByID: {},
|
||||
nodesByPromptID: {},
|
||||
activeNodeID: null,
|
||||
version: 0
|
||||
})
|
||||
@@ -173,6 +177,7 @@ function create() {
|
||||
id: uuidv4(),
|
||||
type: "root",
|
||||
children: [],
|
||||
promptIDs: new Set(),
|
||||
base: { ...params }
|
||||
}
|
||||
s.root = _node
|
||||
@@ -183,6 +188,7 @@ function create() {
|
||||
type: "patch",
|
||||
parent: parentNode,
|
||||
children: [],
|
||||
promptIDs: new Set(),
|
||||
patch: params,
|
||||
}
|
||||
parentNode.children.push(_node);
|
||||
@@ -311,8 +317,16 @@ function create() {
|
||||
return path;
|
||||
}
|
||||
|
||||
function afterQueued(journeyNode: JourneyNode, promptID: PromptID) {
|
||||
journeyNode.promptIDs.add(promptID);
|
||||
store.update(s => {
|
||||
s.nodesByPromptID[promptID] = journeyNode;
|
||||
return s;
|
||||
})
|
||||
}
|
||||
|
||||
function onExecuted(promptID: PromptID, nodeID: ComfyNodeID, output: SerializedPromptOutput, queueEntry: QueueEntry) {
|
||||
const journeyNode = Array.from(iterateBreadthFirst()).find(j => j.promptID === promptID);
|
||||
const journeyNode = get(store).nodesByPromptID[promptID];
|
||||
if (journeyNode == null)
|
||||
return;
|
||||
|
||||
@@ -333,7 +347,8 @@ function create() {
|
||||
selectNode,
|
||||
iterateBreadthFirst,
|
||||
iterateLinearPath,
|
||||
onExecuted
|
||||
afterQueued,
|
||||
onExecuted,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ type QueueStateOps = {
|
||||
export type QueueEntry = {
|
||||
/*** Data preserved on page refresh ***/
|
||||
|
||||
/** Priority of the prompt. -1 means to queue at the front. */
|
||||
/** Priority of the prompt. Lower/negative numbers get higher priority. */
|
||||
number: number,
|
||||
queuedAt?: Date,
|
||||
finishedAt?: Date,
|
||||
|
||||
@@ -132,6 +132,10 @@ function updateFromQueue(queuePending: QueueEntry[], queueRunning: QueueEntry[])
|
||||
// newest entries appear at the top
|
||||
s.queuedEntries = queuePending.map((e) => convertPendingEntry(e, "pending")).reverse();
|
||||
s.runningEntries = queueRunning.map((e) => convertPendingEntry(e, "running")).reverse();
|
||||
|
||||
s.queuedEntries.sort((a, b) => a.entry.number - b.entry.number)
|
||||
s.runningEntries.sort((a, b) => a.entry.number - b.entry.number)
|
||||
|
||||
s.queueUIEntries = s.queuedEntries.concat(s.runningEntries);
|
||||
console.warn("[ComfyQueue] BUILDQUEUE", s.queuedEntries.length, s.runningEntries.length)
|
||||
return s;
|
||||
|
||||
@@ -745,3 +745,7 @@ const MOBILE_USER_AGENTS = ["iPhone", "iPad", "Android", "BlackBerry", "WebOs"].
|
||||
export function isMobileBrowser(userAgent: string): boolean {
|
||||
return MOBILE_USER_AGENTS.some(a => userAgent.match(a))
|
||||
}
|
||||
|
||||
export function isMultiline(input: any, splitLength: number = 50): boolean {
|
||||
return typeof input === "string" && (input.length > splitLength || countNewLines(input) > 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user