Copy action and button
This commit is contained in:
@@ -143,7 +143,7 @@
|
||||
gap: var(--layout-gap);
|
||||
width: var(--size-full);
|
||||
|
||||
.v-pane {
|
||||
> :global(.block > .v-pane) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
&.vertical {
|
||||
position: relative;
|
||||
|
||||
.v-pane {
|
||||
> :global(.block > .v-pane) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
import ComfyQueue from "./ComfyQueue.svelte";
|
||||
import queueState from "$lib/stores/queueState";
|
||||
|
||||
let app: ComfyApp = undefined;
|
||||
export let app: ComfyApp = undefined;
|
||||
let imageViewer: ImageViewer;
|
||||
let queue: ComfyQueue = undefined;
|
||||
let mainElem: HTMLDivElement;
|
||||
let uiPane: ComfyUIPane = undefined;
|
||||
let containerElem: HTMLDivElement;
|
||||
let resizeTimeout: NodeJS.Timeout | null;
|
||||
let hasShownUIHelpToast: boolean = false;
|
||||
|
||||
let debugLayout: boolean = true;
|
||||
|
||||
@@ -44,8 +45,8 @@
|
||||
app.queuePrompt(0, 1);
|
||||
}
|
||||
|
||||
$: if (app) app.lCanvas.allow_dragnodes = !$uiState.nodesLocked;
|
||||
$: if (app) app.lCanvas.allow_interaction = !$uiState.graphLocked;
|
||||
$: if (app?.lCanvas) app.lCanvas.allow_dragnodes = !$uiState.nodesLocked;
|
||||
$: if (app?.lCanvas) app.lCanvas.allow_interaction = !$uiState.graphLocked;
|
||||
|
||||
$: if ($uiState.uiEditMode)
|
||||
$layoutState.currentSelection = []
|
||||
@@ -98,27 +99,26 @@
|
||||
app.lCanvas.recenter();
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
app = new ComfyApp();
|
||||
$: if ($uiState.uiEditMode !== "disabled" && !hasShownUIHelpToast) {
|
||||
hasShownUIHelpToast = true;
|
||||
toast.push("Right-click to open context menu.")
|
||||
}
|
||||
|
||||
if (debugLayout) {
|
||||
layoutState.subscribe(s => {
|
||||
console.warn("UPDATESTATE", s)
|
||||
})
|
||||
}
|
||||
if (debugLayout) {
|
||||
layoutState.subscribe(s => {
|
||||
console.warn("UPDATESTATE", s)
|
||||
})
|
||||
}
|
||||
|
||||
app.api.addEventListener("status", (ev: CustomEvent) => {
|
||||
queueState.statusUpdated(ev.detail as ComfyAPIStatus);
|
||||
});
|
||||
|
||||
await app.setup();
|
||||
(window as any).app = app;
|
||||
(window as any).appPane = uiPane;
|
||||
|
||||
refreshView();
|
||||
app.api.addEventListener("status", (ev: CustomEvent) => {
|
||||
queueState.statusUpdated(ev.detail as ComfyAPIStatus);
|
||||
});
|
||||
|
||||
$: if (app.rootEl && !imageViewer) {
|
||||
imageViewer = new ImageViewer(app.rootEl);
|
||||
}
|
||||
|
||||
$: if (containerElem) {
|
||||
let wrappers = containerElem.querySelectorAll<HTMLDivElement>(".pane-wrapper")
|
||||
for (const wrapper of wrappers) {
|
||||
const paneNode = wrapper.parentNode as HTMLElement; // get the node inside the <Pane/>
|
||||
@@ -126,6 +126,14 @@
|
||||
app.resizeCanvas()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await app.setup();
|
||||
(window as any).app = app;
|
||||
(window as any).appPane = uiPane;
|
||||
|
||||
refreshView();
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LiteGraph, LGraph, LGraphCanvas, LGraphNode, type LGraphNodeConstructor, type LGraphNodeExecutable, type SerializedLGraph, type SerializedLGraphGroup, type SerializedLGraphNode, type SerializedLLink, NodeMode, type Vector2 } from "@litegraph-ts/core";
|
||||
import { LiteGraph, LGraph, LGraphCanvas, LGraphNode, type LGraphNodeConstructor, type LGraphNodeExecutable, type SerializedLGraph, type SerializedLGraphGroup, type SerializedLGraphNode, type SerializedLLink, NodeMode, type Vector2, BuiltInSlotType } from "@litegraph-ts/core";
|
||||
import type { LConnectionKind, INodeSlot } from "@litegraph-ts/core";
|
||||
import ComfyAPI from "$lib/api"
|
||||
import { ComfyWidgets } from "$lib/widgets"
|
||||
@@ -9,7 +9,9 @@ import type TypedEmitter from "typed-emitter";
|
||||
|
||||
// Import nodes
|
||||
import "@litegraph-ts/nodes-basic"
|
||||
import "@litegraph-ts/nodes-events"
|
||||
import * as nodes from "$lib/nodes/index"
|
||||
|
||||
import ComfyGraphCanvas, { type SerializedGraphCanvasState } from "$lib/ComfyGraphCanvas";
|
||||
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
||||
import * as widgets from "$lib/widgets/index"
|
||||
@@ -20,6 +22,7 @@ import type { SerializedLayoutState } from "$lib/stores/layoutState";
|
||||
import layoutState from "$lib/stores/layoutState";
|
||||
import { toast } from '@zerodevx/svelte-toast'
|
||||
import ComfyGraph from "$lib/ComfyGraph";
|
||||
import { ComfyBackendNode } from "$lib/nodes/ComfyBackendNode";
|
||||
|
||||
export const COMFYBOX_SERIAL_VERSION = 1;
|
||||
|
||||
@@ -61,12 +64,18 @@ export default class ComfyApp {
|
||||
|
||||
private queueItems: QueueItem[] = [];
|
||||
private processingQueue: boolean = false;
|
||||
private alreadySetup = false;
|
||||
|
||||
constructor() {
|
||||
this.api = new ComfyAPI();
|
||||
}
|
||||
|
||||
async setup(): Promise<void> {
|
||||
if (this.alreadySetup) {
|
||||
console.error("Already setup")
|
||||
return;
|
||||
}
|
||||
|
||||
this.rootEl = document.getElementById("main") as HTMLDivElement;
|
||||
this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement;
|
||||
this.lGraph = new ComfyGraph();
|
||||
@@ -76,11 +85,7 @@ export default class ComfyApp {
|
||||
LiteGraph.release_link_on_empty_shows_menu = true;
|
||||
LiteGraph.alt_drag_do_clone_nodes = true;
|
||||
|
||||
this.lGraph.start();
|
||||
|
||||
// await this.#invokeExtensionsAsync("init");
|
||||
this.registerNodeTypeOverrides();
|
||||
this.registerWidgetTypeOverrides();
|
||||
await this.registerNodes();
|
||||
|
||||
// Load previous workflow
|
||||
@@ -109,10 +114,7 @@ export default class ComfyApp {
|
||||
this.addPasteHandler();
|
||||
this.addKeyboardHandler();
|
||||
|
||||
// Distinguish frontend/backend connections
|
||||
const BACKEND_TYPES = ["CLIP", "CLIP_VISION", "CLIP_VISION_OUTPUT", "CONDITIONING", "CONTROL_NET", "IMAGE", "LATENT", "MASK", "MODEL", "STYLE_MODEL", "VAE"]
|
||||
for (const type of BACKEND_TYPES)
|
||||
LGraphCanvas.link_type_colors[type] = "orange" // yellow
|
||||
this.setupColorScheme()
|
||||
|
||||
// await this.#invokeExtensionsAsync("setup");
|
||||
|
||||
@@ -120,6 +122,11 @@ export default class ComfyApp {
|
||||
this.resizeCanvas();
|
||||
window.addEventListener("resize", this.resizeCanvas.bind(this));
|
||||
|
||||
this.lGraph.start();
|
||||
this.lGraph.eventBus.on("afterExecute", () => this.lCanvas.draw(true))
|
||||
|
||||
this.alreadySetup = true;
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -137,32 +144,14 @@ export default class ComfyApp {
|
||||
localStorage.setItem("workflow", json)
|
||||
}
|
||||
|
||||
static node_type_overrides: Record<string, typeof ComfyGraphNode> = {}
|
||||
static node_type_overrides: Record<string, typeof ComfyBackendNode> = {}
|
||||
static widget_type_overrides: Record<string, typeof SvelteComponentDev> = {}
|
||||
|
||||
private registerNodeTypeOverrides() {
|
||||
ComfyApp.node_type_overrides["SaveImage"] = nodes.ComfySaveImageNode;
|
||||
ComfyApp.node_type_overrides["PreviewImage"] = nodes.ComfyPreviewImageNode;
|
||||
}
|
||||
|
||||
private registerWidgetTypeOverrides() {
|
||||
ComfyApp.widget_type_overrides["comfy/gallery"] = widgets.ComfyGalleryWidget_Svelte;
|
||||
}
|
||||
|
||||
private async registerNodes() {
|
||||
const app = this;
|
||||
|
||||
// Load node definitions from the backend
|
||||
const defs = await this.api.getNodeDefs();
|
||||
// await this.#invokeExtensionsAsync("addCustomNodeDefs", defs);
|
||||
|
||||
// Generate list of known widgets
|
||||
const widgets = ComfyWidgets;
|
||||
// const widgets = Object.assign(
|
||||
// {},
|
||||
// ComfyWidgets,
|
||||
// ...(await this.#invokeExtensionsAsync("getCustomWidgets")).filter(Boolean)
|
||||
// );
|
||||
|
||||
// Register a node for each definition
|
||||
for (const nodeId in defs) {
|
||||
@@ -171,60 +160,11 @@ export default class ComfyApp {
|
||||
const typeOverride = ComfyApp.node_type_overrides[nodeId]
|
||||
if (typeOverride)
|
||||
console.debug("Attaching custom type to received node:", nodeId, typeOverride)
|
||||
const baseClass: typeof LGraphNode = typeOverride || LGraphNode;
|
||||
const baseClass: typeof ComfyBackendNode = typeOverride || ComfyBackendNode;
|
||||
|
||||
const ctor = class extends baseClass {
|
||||
constructor(title?: string) {
|
||||
super(title);
|
||||
this.type = nodeId; // XXX: workaround dependency in LGraphNode.addInput()
|
||||
(this as any).comfyClass = nodeId;
|
||||
(this as any).isBackendNode = true;
|
||||
const color = LGraphCanvas.node_colors["yellow"];
|
||||
this.color = color.color
|
||||
this.bgColor = color.bgColor
|
||||
var inputs = nodeData["input"]["required"];
|
||||
if (nodeData["input"]["optional"] != undefined) {
|
||||
inputs = Object.assign({}, nodeData["input"]["required"], nodeData["input"]["optional"])
|
||||
}
|
||||
const config = { minWidth: 1, minHeight: 1 };
|
||||
for (const inputName in inputs) {
|
||||
const inputData = inputs[inputName];
|
||||
const type = inputData[0];
|
||||
|
||||
if (inputData[1]?.forceInput) {
|
||||
this.addInput(inputName, type);
|
||||
} else {
|
||||
if (Array.isArray(type)) {
|
||||
// Enums
|
||||
Object.assign(config, widgets.COMBO(this, inputName, inputData, app) || {});
|
||||
} else if (`${type}:${inputName}` in widgets) {
|
||||
// Support custom widgets by Type:Name
|
||||
Object.assign(config, widgets[`${type}:${inputName}`](this, inputName, inputData, app) || {});
|
||||
} else if (type in widgets) {
|
||||
// Standard type widgets
|
||||
Object.assign(config, widgets[type](this, inputName, inputData, app) || {});
|
||||
} else {
|
||||
// Node connection inputs (backend)
|
||||
this.addInput(inputName, type, { color_off: "orange", color_on: "orange" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const o in nodeData["output"]) {
|
||||
const output = nodeData["output"][o];
|
||||
const outputName = nodeData["output_name"][o] || output;
|
||||
this.addOutput(outputName, output, { color_off: "orange", color_on: "orange" });
|
||||
}
|
||||
|
||||
const s = this.computeSize();
|
||||
s[0] = Math.max(config.minWidth, s[0] * 1.5);
|
||||
s[1] = Math.max(config.minHeight, s[1]);
|
||||
this.size = s;
|
||||
this.serialize_widgets = false;
|
||||
|
||||
// app.#invokeExtensionsAsync("nodeCreated", this);
|
||||
|
||||
return this;
|
||||
super(title, nodeId, nodeData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,15 +175,9 @@ export default class ComfyApp {
|
||||
desc: `ComfyNode: ${nodeId}`
|
||||
}
|
||||
|
||||
// this.#addNodeContextMenuHandler(node);
|
||||
// this.#addDrawBackgroundHandler(node, app);
|
||||
|
||||
// await this.#invokeExtensionsAsync("beforeRegisterNodeDef", node, nodeData);
|
||||
LiteGraph.registerNodeType(node);
|
||||
node.category = nodeData.category;
|
||||
}
|
||||
|
||||
// await this.#invokeExtensionsAsync("registerCustomNodes");
|
||||
}
|
||||
|
||||
private showDropZone() {
|
||||
@@ -363,6 +297,23 @@ export default class ComfyApp {
|
||||
});
|
||||
}
|
||||
|
||||
private setupColorScheme() {
|
||||
const setColor = (type: any, color: string) => {
|
||||
this.lCanvas.link_type_colors[type] = color
|
||||
this.lCanvas.default_connection_color_byType[type] = color
|
||||
}
|
||||
|
||||
// Distinguish frontend/backend connections
|
||||
const BACKEND_TYPES = ["CLIP", "CLIP_VISION", "CLIP_VISION_OUTPUT", "CONDITIONING", "CONTROL_NET", "IMAGE", "LATENT", "MASK", "MODEL", "STYLE_MODEL", "VAE"]
|
||||
for (const type of BACKEND_TYPES) {
|
||||
setColor(type, "orange")
|
||||
}
|
||||
|
||||
setColor("OUTPUT", "rebeccapurple")
|
||||
setColor(BuiltInSlotType.EVENT, "lightseagreen")
|
||||
setColor(BuiltInSlotType.ACTION, "lightseagreen")
|
||||
}
|
||||
|
||||
serialize(): SerializedAppState {
|
||||
const graph = this.lGraph;
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
// TODO
|
||||
}
|
||||
|
||||
function groupWidgets() {
|
||||
function groupWidgets(horizontal: boolean) {
|
||||
const items = layoutState.getCurrentSelection()
|
||||
$layoutState.currentSelection = []
|
||||
layoutState.groupItems(items)
|
||||
layoutState.groupItems(items, { direction: horizontal ? "horizontal" : "vertical" })
|
||||
}
|
||||
|
||||
let canUngroup = false;
|
||||
@@ -95,8 +95,12 @@
|
||||
<Menu {...menuPos} on:click={closeMenu} on:clickoutside={closeMenu}>
|
||||
<MenuOption
|
||||
isDisabled={$layoutState.currentSelection.length === 0}
|
||||
on:click={groupWidgets}
|
||||
on:click={() => groupWidgets(false)}
|
||||
text="Group" />
|
||||
<MenuOption
|
||||
isDisabled={$layoutState.currentSelection.length === 0}
|
||||
on:click={() => groupWidgets(true)}
|
||||
text="Group Horizontally" />
|
||||
<MenuOption
|
||||
isDisabled={!canUngroup}
|
||||
on:click={ungroup}
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
.widget.selected {
|
||||
background: var(--color-yellow-200);
|
||||
}
|
||||
.container.selected {
|
||||
background: var(--color-yellow-400);
|
||||
}
|
||||
|
||||
.is-executing {
|
||||
border: 3px dashed var(--color-green-600) !important;
|
||||
|
||||
Reference in New Issue
Block a user