History fixes

This commit is contained in:
space-nuko
2023-06-03 20:11:24 -05:00
parent 9054d257af
commit 94d96375cb
11 changed files with 125 additions and 56 deletions

View File

@@ -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) {

View File

@@ -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" })

View File

@@ -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);

View File

@@ -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

View File

@@ -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",
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -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,
}
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);
}