Use UUIDs everywhere & improve prompt popup

This commit is contained in:
space-nuko
2023-05-13 01:20:58 -05:00
parent 7786a0aba6
commit 23271197b5
12 changed files with 6064 additions and 2522 deletions

View File

@@ -1,6 +1,6 @@
import { LiteGraph, LGraph, LGraphCanvas, LGraphNode, type LGraphNodeConstructor, type LGraphNodeExecutable, type SerializedLGraph, type SerializedLGraphGroup, type SerializedLGraphNode, type SerializedLLink, NodeMode, type Vector2, BuiltInSlotType, type INodeInputSlot } from "@litegraph-ts/core";
import type { LConnectionKind, INodeSlot } from "@litegraph-ts/core";
import ComfyAPI, { type ComfyAPIStatusResponse, type ComfyPromptExtraData, type ComfyPromptRequest, type NodeID, type PromptID } from "$lib/api"
import ComfyAPI, { type ComfyAPIStatusResponse, type ComfyBoxPromptExtraData, type ComfyPromptRequest, type NodeID, type PromptID } from "$lib/api"
import { getPngMetadata, importA1111 } from "$lib/pnginfo";
import EventEmitter from "events";
import type TypedEmitter from "typed-emitter";
@@ -349,7 +349,7 @@ export default class ComfyApp {
this.api.addEventListener("executed", (promptID: PromptID, nodeID: NodeID, output: GalleryOutput) => {
this.nodeOutputs[nodeID] = output;
const node = this.lGraph.getNodeById(parseInt(nodeID)) as ComfyGraphNode;
const node = this.lGraph.getNodeById(nodeID) as ComfyGraphNode;
if (node?.onExecuted) {
node.onExecuted(output);
}
@@ -467,7 +467,6 @@ export default class ComfyApp {
state = structuredClone(blankGraph)
}
await this.deserialize(state)
uiState.update(s => { s.uiUnlocked = true; return s; })
}
/**
@@ -558,7 +557,7 @@ export default class ComfyApp {
* Converts the current graph workflow for sending to the API
* @returns The workflow and node links
*/
async graphToPrompt(tag: string | null = null): Promise<SerializedPrompt> {
graphToPrompt(tag: string | null = null): SerializedPrompt {
// Run frontend-only logic
this.lGraph.runStep(1)
@@ -754,14 +753,16 @@ export default class ComfyApp {
}
}
const p = await this.graphToPrompt(tag);
const p = this.graphToPrompt(tag);
const l = layoutState.serialize();
console.debug(promptToGraphVis(p))
const extraData: ComfyPromptExtraData = {
const extraData: ComfyBoxPromptExtraData = {
extra_pnginfo: {
workflow: p.workflow
workflow: p.workflow,
comfyBoxLayout: l,
comfyBoxSubgraphs: [tag],
},
subgraphs: [tag],
thumbnails
}

View File

@@ -157,8 +157,10 @@
let showModal = false;
let selectedPrompt = null;
let selectedImages = [];
function showPrompt(entry: QueueUIEntry, e: MouseEvent) {
selectedPrompt = entry.entry.prompt;
selectedImages = entry.images;
showModal = true;
}
@@ -178,7 +180,7 @@
<h1 style="padding-bottom: 1rem;">Prompt Details</h1>
</div>
{#if selectedPrompt}
<PromptDisplay prompt={selectedPrompt} />
<PromptDisplay prompt={selectedPrompt} images={selectedImages} />
{/if}
</Modal>
@@ -495,6 +497,9 @@
display: flex;
justify-content: center;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.queue-remaining {

View File

@@ -1,15 +1,22 @@
<script lang="ts">
import { fade } from "svelte/transition";
import { TextBox } from "@gradio/form";
import type { SerializedPromptInput, SerializedPromptInputsAll } from "./ComfyApp";
import { Block, BlockLabel, BlockTitle } from "@gradio/atoms";
import { JSON as JSONComponent } from "@gradio/json";
import { JSON as JSONIcon, Copy, Check } from "@gradio/icons";
import Accordion from "$lib/components/gradio/app/Accordion.svelte";
import Gallery from "$lib/components/gradio/gallery/Gallery.svelte";
import type { Styles } from "@gradio/utils";
const splitLength = 50;
export let prompt: SerializedPromptInputsAll;
export let images: string[] = [];
let galleryStyle: Styles = {
grid_cols: [2],
object_fit: "cover",
}
function isInputLink(input: SerializedPromptInput): boolean {
return Array.isArray(input)
@@ -55,63 +62,98 @@
</script>
<div class="prompt-display">
<Block>
{#each Object.entries(prompt) as [nodeID, inputs], i}
{@const classType = inputs.class_type}
{@const filtered = Object.entries(inputs.inputs).filter((i) => !isInputLink(i[1]))}
{#if filtered.length > 0}
<div class="accordion">
<Block padding={true}>
<Accordion label="Node {nodeID}: {classType}" open={false}>
{#each filtered as [inputName, input]}
<Block>
<BlockTitle>Input: {inputName}</BlockTitle>
<button class="copy-button" on:click={() => handleCopy(nodeID, inputName, input)}>
{#if copiedNodeID === nodeID && copiedInputName === inputName}
<span class="copied-icon">
<Check />
</span>
{:else}
<span class="copy-text"><Copy /></span>
{/if}
</button>
<div>
{#if isInputLink(input)}
Link {input[0]} -> {input[1]}
{:else if typeof input === "object"}
<Block>
<BlockLabel
Icon={JSONIcon}
show_label={true}
label={inputName}
float={true}
/>
<JSONComponent value={input} />
</Block>
{:else if isMultiline(input)}
{@const lines = Math.max(countNewLines(input), input.length / splitLength)}
<TextBox label={inputName} value={formatInput(input)} {lines} max_lines={lines} />
{:else}
<TextBox label={inputName} value={formatInput(input)} lines={1} max_lines={1} />
{/if}
</div>
</Block>
{/each}
</Accordion>
</Block>
</div>
{/if}
{/each}
</Block>
<div class="scroll-container">
<Block>
{#each Object.entries(prompt) as [nodeID, inputs], i}
{@const classType = inputs.class_type}
{@const filtered = Object.entries(inputs.inputs).filter((i) => !isInputLink(i[1]))}
{#if filtered.length > 0}
<div class="accordion">
<Block padding={true}>
<Accordion label="Node {i+1}: {classType}" open={false}>
{#each filtered as [inputName, input]}
<Block>
<button class="copy-button" on:click={() => handleCopy(nodeID, inputName, input)}>
{#if copiedNodeID === nodeID && copiedInputName === inputName}
<span class="copied-icon">
<Check />
</span>
{:else}
<span class="copy-text"><Copy /></span>
{/if}
</button>
<div>
{#if isInputLink(input)}
Link {input[0]} -> {input[1]}
{:else if typeof input === "object"}
<Block>
<BlockLabel
Icon={JSONIcon}
show_label={true}
label={inputName}
float={true}
/>
<JSONComponent value={input} />
</Block>
{:else if isMultiline(input)}
{@const lines = Math.max(countNewLines(input), input.length / splitLength)}
<TextBox label={inputName} value={formatInput(input)} {lines} max_lines={lines} />
{:else}
<TextBox label={inputName} value={formatInput(input)} lines={1} max_lines={1} />
{/if}
</div>
</Block>
{/each}
</Accordion>
</Block>
</div>
{/if}
{/each}
</Block>
</div>
{#if images.length > 0}
<div class="image-container">
<Block>
<Gallery
value={images}
label=""
show_label={false}
style={galleryStyle}
root={""}
root_url={""}
/>
</Block>
</div>
{/if}
</div>
<style lang="scss">
.prompt-display {
overflow-y: scroll;
width: 50vw;
width: 70vw;
height: 60vh;
color: none;
display: flex;
flex-wrap: nowrap;
overflow-y: auto;
flex-direction: column;
@media (min-width: 1600px) {
flex-direction: row;
}
.scroll-container {
position: relative;
/* overflow-y: auto; */
flex: 1 1 0%;
}
.image-container {
position: relative;
flex: 1 1 0%;
max-width: 30vw;
}
.copy-button {
display: flex;
position: absolute;