Start serializing UI state
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
"build:css": "pollen -c gradio/js/theme/src/pollen.config.cjs && mv src/pollen.css node_modules/@gradio/theme/src"
|
"build:css": "pollen -c gradio/js/theme/src/pollen.config.cjs && mv src/pollen.css node_modules/@gradio/theme/src"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@zerodevx/svelte-toast": "^0.9.3",
|
||||||
"eslint": "^8.37.0",
|
"eslint": "^8.37.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-svelte3": "^4.0.0",
|
"eslint-plugin-svelte3": "^4.0.0",
|
||||||
|
|||||||
102
pnpm-lock.yaml
generated
102
pnpm-lock.yaml
generated
@@ -63,7 +63,7 @@ importers:
|
|||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
svelte-preprocess:
|
svelte-preprocess:
|
||||||
specifier: ^5.0.3
|
specifier: ^5.0.3
|
||||||
version: 5.0.3(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3)
|
version: 5.0.3(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3)
|
||||||
svelte-select:
|
svelte-select:
|
||||||
specifier: ^5.5.3
|
specifier: ^5.5.3
|
||||||
version: 5.5.3
|
version: 5.5.3
|
||||||
@@ -72,7 +72,7 @@ importers:
|
|||||||
version: 0.7.13(svelte@3.58.0)
|
version: 0.7.13(svelte@3.58.0)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^3.3.1
|
specifier: ^3.3.1
|
||||||
version: 3.3.1(postcss@8.4.21)
|
version: 3.3.1
|
||||||
typed-emitter:
|
typed-emitter:
|
||||||
specifier: github:andywer/typed-emitter
|
specifier: github:andywer/typed-emitter
|
||||||
version: github.com/andywer/typed-emitter/9a139b6fa0ec6b0db6141b5b756b784e4f7ef4e4
|
version: github.com/andywer/typed-emitter/9a139b6fa0ec6b0db6141b5b756b784e4f7ef4e4
|
||||||
@@ -80,6 +80,9 @@ importers:
|
|||||||
specifier: ^1.0.5
|
specifier: ^1.0.5
|
||||||
version: 1.0.5(vite@4.3.1)
|
version: 1.0.5(vite@4.3.1)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@zerodevx/svelte-toast':
|
||||||
|
specifier: ^0.9.3
|
||||||
|
version: 0.9.3(svelte@3.58.0)
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^8.37.0
|
specifier: ^8.37.0
|
||||||
version: 8.37.0
|
version: 8.37.0
|
||||||
@@ -103,7 +106,7 @@ importers:
|
|||||||
version: 3.58.0
|
version: 3.58.0
|
||||||
svelte-check:
|
svelte-check:
|
||||||
specifier: ^3.2.0
|
specifier: ^3.2.0
|
||||||
version: 3.2.0(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)
|
version: 3.2.0(sass@1.61.0)(svelte@3.58.0)
|
||||||
svelte-dnd-action:
|
svelte-dnd-action:
|
||||||
specifier: ^0.9.22
|
specifier: ^0.9.22
|
||||||
version: 0.9.22(svelte@3.58.0)
|
version: 0.9.22(svelte@3.58.0)
|
||||||
@@ -112,7 +115,7 @@ importers:
|
|||||||
version: 5.0.3
|
version: 5.0.3
|
||||||
vite:
|
vite:
|
||||||
specifier: ^4.3.1
|
specifier: ^4.3.1
|
||||||
version: 4.3.1(@types/node@18.16.0)(sass@1.61.0)
|
version: 4.3.1(sass@1.61.0)
|
||||||
vite-tsconfig-paths:
|
vite-tsconfig-paths:
|
||||||
specifier: ^4.0.8
|
specifier: ^4.0.8
|
||||||
version: 4.0.8(typescript@5.0.3)(vite@4.3.1)
|
version: 4.0.8(typescript@5.0.3)(vite@4.3.1)
|
||||||
@@ -2035,7 +2038,7 @@ packages:
|
|||||||
magic-string: 0.30.0
|
magic-string: 0.30.0
|
||||||
svelte: 3.58.0
|
svelte: 3.58.0
|
||||||
svelte-hmr: 0.15.1(svelte@3.58.0)
|
svelte-hmr: 0.15.1(svelte@3.58.0)
|
||||||
vite: 4.3.1(@types/node@18.16.0)(sass@1.61.0)
|
vite: 4.3.1(sass@1.61.0)
|
||||||
vitefu: 0.2.4(vite@4.3.1)
|
vitefu: 0.2.4(vite@4.3.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -2178,6 +2181,7 @@ packages:
|
|||||||
|
|
||||||
/@types/node@18.16.0:
|
/@types/node@18.16.0:
|
||||||
resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==}
|
resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node@8.10.66:
|
/@types/node@8.10.66:
|
||||||
resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==}
|
resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==}
|
||||||
@@ -2281,6 +2285,14 @@ packages:
|
|||||||
eslint-visitor-keys: 3.4.0
|
eslint-visitor-keys: 3.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@zerodevx/svelte-toast@0.9.3(svelte@3.58.0):
|
||||||
|
resolution: {integrity: sha512-VPKWR4A9y01fyXRscu9HiTj7tV2hFrpRKZvGwMmaPXfHIXR1D9+NNsz0HXcQ7qZ0C5UaHS3n9uNtPtIcAXT7RQ==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^3.57.0
|
||||||
|
dependencies:
|
||||||
|
svelte: 3.58.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/acorn-jsx@5.3.2(acorn@8.8.2):
|
/acorn-jsx@5.3.2(acorn@8.8.2):
|
||||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -5911,7 +5923,7 @@ packages:
|
|||||||
- sugarss
|
- sugarss
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/svelte-check@3.2.0(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0):
|
/svelte-check@3.2.0(sass@1.61.0)(svelte@3.58.0):
|
||||||
resolution: {integrity: sha512-6ZnscN8dHEN5Eq5LgIzjj07W9nc9myyBH+diXsUAuiY/3rt0l65/LCIQYlIuoFEjp2F1NhXqZiJwV9omPj9tMw==}
|
resolution: {integrity: sha512-6ZnscN8dHEN5Eq5LgIzjj07W9nc9myyBH+diXsUAuiY/3rt0l65/LCIQYlIuoFEjp2F1NhXqZiJwV9omPj9tMw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -5924,7 +5936,7 @@ packages:
|
|||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
sade: 1.8.1
|
sade: 1.8.1
|
||||||
svelte: 3.58.0
|
svelte: 3.58.0
|
||||||
svelte-preprocess: 5.0.3(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3)
|
svelte-preprocess: 5.0.3(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3)
|
||||||
typescript: 5.0.3
|
typescript: 5.0.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
@@ -6084,7 +6096,7 @@ packages:
|
|||||||
typescript: 5.0.3
|
typescript: 5.0.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/svelte-preprocess@5.0.3(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3):
|
/svelte-preprocess@5.0.3(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3):
|
||||||
resolution: {integrity: sha512-GrHF1rusdJVbOZOwgPWtpqmaexkydznKzy5qIC2FabgpFyKN57bjMUUUqPRfbBXK5igiEWn1uO/DXsa2vJ5VHA==}
|
resolution: {integrity: sha512-GrHF1rusdJVbOZOwgPWtpqmaexkydznKzy5qIC2FabgpFyKN57bjMUUUqPRfbBXK5igiEWn1uO/DXsa2vJ5VHA==}
|
||||||
engines: {node: '>= 14.10.0'}
|
engines: {node: '>= 14.10.0'}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
@@ -6125,7 +6137,6 @@ packages:
|
|||||||
'@types/pug': 2.0.6
|
'@types/pug': 2.0.6
|
||||||
detect-indent: 6.1.0
|
detect-indent: 6.1.0
|
||||||
magic-string: 0.27.0
|
magic-string: 0.27.0
|
||||||
postcss: 8.4.21
|
|
||||||
sass: 1.61.0
|
sass: 1.61.0
|
||||||
sorcery: 0.11.0
|
sorcery: 0.11.0
|
||||||
strip-indent: 3.0.0
|
strip-indent: 3.0.0
|
||||||
@@ -6190,6 +6201,39 @@ packages:
|
|||||||
get-port: 3.2.0
|
get-port: 3.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/tailwindcss@3.3.1:
|
||||||
|
resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==}
|
||||||
|
engines: {node: '>=12.13.0'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
arg: 5.0.2
|
||||||
|
chokidar: 3.5.3
|
||||||
|
color-name: 1.1.4
|
||||||
|
didyoumean: 1.2.2
|
||||||
|
dlv: 1.1.3
|
||||||
|
fast-glob: 3.2.12
|
||||||
|
glob-parent: 6.0.2
|
||||||
|
is-glob: 4.0.3
|
||||||
|
jiti: 1.18.2
|
||||||
|
lilconfig: 2.1.0
|
||||||
|
micromatch: 4.0.5
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
object-hash: 3.0.0
|
||||||
|
picocolors: 1.0.0
|
||||||
|
postcss: 8.4.21
|
||||||
|
postcss-import: 14.1.0(postcss@8.4.21)
|
||||||
|
postcss-js: 4.0.1(postcss@8.4.21)
|
||||||
|
postcss-load-config: 3.1.4(postcss@8.4.21)
|
||||||
|
postcss-nested: 6.0.0(postcss@8.4.21)
|
||||||
|
postcss-selector-parser: 6.0.11
|
||||||
|
postcss-value-parser: 4.2.0
|
||||||
|
quick-lru: 5.1.1
|
||||||
|
resolve: 1.22.2
|
||||||
|
sucrase: 3.32.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- ts-node
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tailwindcss@3.3.1(postcss@8.4.21):
|
/tailwindcss@3.3.1(postcss@8.4.21):
|
||||||
resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==}
|
resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==}
|
||||||
engines: {node: '>=12.13.0'}
|
engines: {node: '>=12.13.0'}
|
||||||
@@ -6223,6 +6267,7 @@ packages:
|
|||||||
sucrase: 3.32.0
|
sucrase: 3.32.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- ts-node
|
- ts-node
|
||||||
|
dev: true
|
||||||
|
|
||||||
/test-exclude@6.0.0:
|
/test-exclude@6.0.0:
|
||||||
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
|
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
|
||||||
@@ -6961,7 +7006,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
vite: 4.3.1(@types/node@18.16.0)(sass@1.61.0)
|
vite: 4.3.1(sass@1.61.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vite-tsconfig-paths@4.0.8(typescript@5.0.3)(vite@4.3.1):
|
/vite-tsconfig-paths@4.0.8(typescript@5.0.3)(vite@4.3.1):
|
||||||
@@ -6975,7 +7020,7 @@ packages:
|
|||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
globrex: 0.1.2
|
globrex: 0.1.2
|
||||||
tsconfck: 2.1.1(typescript@5.0.3)
|
tsconfck: 2.1.1(typescript@5.0.3)
|
||||||
vite: 4.3.1(@types/node@18.16.0)(sass@1.61.0)
|
vite: 4.3.1(sass@1.61.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
@@ -7071,6 +7116,39 @@ packages:
|
|||||||
sass: 1.61.0
|
sass: 1.61.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/vite@4.3.1(sass@1.61.0):
|
||||||
|
resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==}
|
||||||
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>= 14'
|
||||||
|
less: '*'
|
||||||
|
sass: '*'
|
||||||
|
stylus: '*'
|
||||||
|
sugarss: '*'
|
||||||
|
terser: ^5.4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
less:
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
stylus:
|
||||||
|
optional: true
|
||||||
|
sugarss:
|
||||||
|
optional: true
|
||||||
|
terser:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.17.18
|
||||||
|
postcss: 8.4.21
|
||||||
|
rollup: 3.21.0
|
||||||
|
sass: 1.61.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
/vitefu@0.2.4(vite@4.3.1):
|
/vitefu@0.2.4(vite@4.3.1):
|
||||||
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
|
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
|
||||||
@@ -7080,7 +7158,7 @@ packages:
|
|||||||
vite:
|
vite:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 4.3.1(@types/node@18.16.0)(sass@1.61.0)
|
vite: 4.3.1(sass@1.61.0)
|
||||||
|
|
||||||
/vitest@0.25.8(sass@1.61.0):
|
/vitest@0.25.8(sass@1.61.0):
|
||||||
resolution: {integrity: sha512-X75TApG2wZTJn299E/TIYevr4E9/nBo1sUtZzn0Ci5oK8qnpZAZyhwg0qCeMSakGIWtc6oRwcQFyFfW14aOFWg==}
|
resolution: {integrity: sha512-X75TApG2wZTJn299E/TIYevr4E9/nBo1sUtZzn0Ci5oK8qnpZAZyhwg0qCeMSakGIWtc6oRwcQFyFfW14aOFWg==}
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import type ComfyApp from "./components/ComfyApp";
|
|||||||
import queueState from "./stores/queueState";
|
import queueState from "./stores/queueState";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
|
export type SerializedGraphCanvasState = {
|
||||||
|
offset: Vector2,
|
||||||
|
scale: number
|
||||||
|
}
|
||||||
|
|
||||||
export default class ComfyGraphCanvas extends LGraphCanvas {
|
export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||||
app: ComfyApp
|
app: ComfyApp
|
||||||
|
|
||||||
@@ -20,6 +25,23 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
|||||||
this.app = app;
|
this.app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serialize(): SerializedGraphCanvasState {
|
||||||
|
return {
|
||||||
|
offset: this.ds.offset,
|
||||||
|
scale: this.ds.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize(data: SerializedGraphCanvasState) {
|
||||||
|
this.ds.offset = data.offset;
|
||||||
|
this.ds.scale = data.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
recenter() {
|
||||||
|
this.ds.reset();
|
||||||
|
this.setDirty(true, true)
|
||||||
|
}
|
||||||
|
|
||||||
override drawNodeShape(
|
override drawNodeShape(
|
||||||
node: LGraphNode,
|
node: LGraphNode,
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
import layoutState from "$lib/stores/layoutState";
|
import layoutState from "$lib/stores/layoutState";
|
||||||
import { ImageViewer } from "$lib/ImageViewer";
|
import { ImageViewer } from "$lib/ImageViewer";
|
||||||
import { download } from "$lib/utils"
|
|
||||||
import type { ComfyAPIStatus } from "$lib/api";
|
import type { ComfyAPIStatus } from "$lib/api";
|
||||||
|
import { SvelteToast, toast } from '@zerodevx/svelte-toast'
|
||||||
|
|
||||||
import { LGraph } from "@litegraph-ts/core";
|
import { LGraph } from "@litegraph-ts/core";
|
||||||
import LightboxModal from "./LightboxModal.svelte";
|
import LightboxModal from "./LightboxModal.svelte";
|
||||||
@@ -19,11 +19,20 @@
|
|||||||
|
|
||||||
let app: ComfyApp = undefined;
|
let app: ComfyApp = undefined;
|
||||||
let imageViewer: ImageViewer;
|
let imageViewer: ImageViewer;
|
||||||
let uiPane: ComfyUIPane = undefined;
|
|
||||||
let queue: ComfyQueue = undefined;
|
let queue: ComfyQueue = undefined;
|
||||||
let mainElem: HTMLDivElement;
|
let mainElem: HTMLDivElement;
|
||||||
|
let uiPane: ComfyUIPane = undefined;
|
||||||
let containerElem: HTMLDivElement;
|
let containerElem: HTMLDivElement;
|
||||||
let resizeTimeout: typeof Timer = -1;
|
let resizeTimeout: NodeJS.Timeout | null;
|
||||||
|
|
||||||
|
let debugLayout: boolean = true;
|
||||||
|
|
||||||
|
const toastOptions = {
|
||||||
|
intro: { duration: 200 },
|
||||||
|
theme: {
|
||||||
|
'--toastBarHeight': 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function refreshView(event?: Event) {
|
function refreshView(event?: Event) {
|
||||||
clearTimeout(resizeTimeout);
|
clearTimeout(resizeTimeout);
|
||||||
@@ -65,35 +74,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function doAutosave(graph: LGraph): void {
|
|
||||||
const savedWorkflow = app.serialize();
|
|
||||||
localStorage.setItem("workflow", JSON.stringify(savedWorkflow))
|
|
||||||
}
|
|
||||||
|
|
||||||
function doRestore(data: SerializedAppState) {
|
|
||||||
layoutState.deserialize(data.layout, app.lGraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
function doSave(): void {
|
function doSave(): void {
|
||||||
if (!app?.lGraph)
|
if (!app?.lGraph)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const date = new Date();
|
app.saveStateToLocalStorage();
|
||||||
const formattedDate = date.toISOString().replace(/:/g, '-').replace(/\.\d{3}/g, '').replace('T', '_').replace("Z", "");
|
toast.push("Saved to local storage.")
|
||||||
|
//
|
||||||
|
// const date = new Date();
|
||||||
|
// const formattedDate = date.toISOString().replace(/:/g, '-').replace(/\.\d{3}/g, '').replace('T', '_').replace("Z", "");
|
||||||
|
//
|
||||||
|
// download(`workflow-${formattedDate}.json`, JSON.stringify(app.serialize()), "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
download(`workflow-${formattedDate}.json`, JSON.stringify(app.serialize()), "application/json")
|
function doReset(): void {
|
||||||
|
var confirmed = confirm("Are you sure you want to clear the current workflow?");
|
||||||
|
if (confirmed) {
|
||||||
|
app.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doRecenter(): void {
|
||||||
|
app.lCanvas.recenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
app = new ComfyApp();
|
app = new ComfyApp();
|
||||||
|
|
||||||
|
if (debugLayout) {
|
||||||
|
layoutState.subscribe(s => {
|
||||||
|
console.warn("UPDATESTATE", s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
app.eventBus.on("nodeAdded", layoutState.nodeAdded);
|
app.eventBus.on("nodeAdded", layoutState.nodeAdded);
|
||||||
app.eventBus.on("nodeRemoved", layoutState.nodeRemoved);
|
app.eventBus.on("nodeRemoved", layoutState.nodeRemoved);
|
||||||
app.eventBus.on("configured", layoutState.configureFinished);
|
|
||||||
app.eventBus.on("cleared", layoutState.clear);
|
|
||||||
|
|
||||||
app.eventBus.on("autosave", doAutosave);
|
|
||||||
app.eventBus.on("restored", doRestore);
|
|
||||||
|
|
||||||
app.api.addEventListener("status", (ev: CustomEvent) => {
|
app.api.addEventListener("status", (ev: CustomEvent) => {
|
||||||
queueState.statusUpdated(ev.detail as ComfyAPIStatus);
|
queueState.statusUpdated(ev.detail as ComfyAPIStatus);
|
||||||
@@ -117,7 +132,7 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="main" bind:this={mainElem}>
|
<div id="main">
|
||||||
<div id="dropzone" class="dropzone"></div>
|
<div id="dropzone" class="dropzone"></div>
|
||||||
<div id="container" bind:this={containerElem}>
|
<div id="container" bind:this={containerElem}>
|
||||||
<Splitpanes theme="comfy" on:resize={refreshView}>
|
<Splitpanes theme="comfy" on:resize={refreshView}>
|
||||||
@@ -153,6 +168,12 @@
|
|||||||
<Button variant="secondary" on:click={doSave}>
|
<Button variant="secondary" on:click={doSave}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button variant="secondary" on:click={doReset}>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" on:click={doRecenter}>
|
||||||
|
Recenter
|
||||||
|
</Button>
|
||||||
<Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
|
<Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
|
||||||
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/>
|
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/>
|
||||||
<label for="enable-ui-editing">Enable UI Editing</label>
|
<label for="enable-ui-editing">Enable UI Editing</label>
|
||||||
@@ -164,6 +185,8 @@
|
|||||||
<LightboxModal />
|
<LightboxModal />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<SvelteToast options={toastOptions} />
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#container {
|
#container {
|
||||||
height: calc(100vh - 60px);
|
height: calc(100vh - 60px);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LiteGraph, LGraph, LGraphCanvas, LGraphNode, type LGraphNodeConstructor, type LGraphNodeExecutable, type SerializedLGraph, type SerializedLGraphGroup, type SerializedLGraphNode, type SerializedLLink, NodeMode } from "@litegraph-ts/core";
|
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 type { LConnectionKind, INodeSlot } from "@litegraph-ts/core";
|
import type { LConnectionKind, INodeSlot } from "@litegraph-ts/core";
|
||||||
import ComfyAPI from "$lib/api"
|
import ComfyAPI from "$lib/api"
|
||||||
import { ComfyWidgets } from "$lib/widgets"
|
import { ComfyWidgets } from "$lib/widgets"
|
||||||
@@ -10,7 +10,7 @@ import type TypedEmitter from "typed-emitter";
|
|||||||
// Import nodes
|
// Import nodes
|
||||||
import "@litegraph-ts/nodes-basic"
|
import "@litegraph-ts/nodes-basic"
|
||||||
import * as nodes from "$lib/nodes/index"
|
import * as nodes from "$lib/nodes/index"
|
||||||
import ComfyGraphCanvas from "$lib/ComfyGraphCanvas";
|
import ComfyGraphCanvas, { type SerializedGraphCanvasState } from "$lib/ComfyGraphCanvas";
|
||||||
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
||||||
import * as widgets from "$lib/widgets/index"
|
import * as widgets from "$lib/widgets/index"
|
||||||
import type ComfyWidget from "$lib/widgets/ComfyWidget";
|
import type ComfyWidget from "$lib/widgets/ComfyWidget";
|
||||||
@@ -21,6 +21,8 @@ import type IComfyInputSlot from "$lib/IComfyInputSlot";
|
|||||||
import type { SerializedLayoutState } from "$lib/stores/layoutState";
|
import type { SerializedLayoutState } from "$lib/stores/layoutState";
|
||||||
import layoutState from "$lib/stores/layoutState";
|
import layoutState from "$lib/stores/layoutState";
|
||||||
|
|
||||||
|
export const COMFYBOX_SERIAL_VERSION = 1;
|
||||||
|
|
||||||
LiteGraph.catch_exceptions = false;
|
LiteGraph.catch_exceptions = false;
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
@@ -34,7 +36,8 @@ export type SerializedAppState = {
|
|||||||
createdBy: "ComfyBox",
|
createdBy: "ComfyBox",
|
||||||
version: number,
|
version: number,
|
||||||
workflow: SerializedLGraph,
|
workflow: SerializedLGraph,
|
||||||
layout: SerializedLayoutState
|
layout: SerializedLayoutState,
|
||||||
|
canvas: SerializedGraphCanvasState
|
||||||
}
|
}
|
||||||
|
|
||||||
type ComfyAppEvents = {
|
type ComfyAppEvents = {
|
||||||
@@ -45,7 +48,6 @@ type ComfyAppEvents = {
|
|||||||
cleared: () => void
|
cleared: () => void
|
||||||
beforeChange: (graph: LGraph, param: any) => void
|
beforeChange: (graph: LGraph, param: any) => void
|
||||||
afterChange: (graph: LGraph, param: any) => void
|
afterChange: (graph: LGraph, param: any) => void
|
||||||
autosave: (graph: LGraph) => void
|
|
||||||
restored: (workflow: SerializedAppState) => void
|
restored: (workflow: SerializedAppState) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +62,7 @@ export default class ComfyApp {
|
|||||||
canvasEl: HTMLCanvasElement | null = null;
|
canvasEl: HTMLCanvasElement | null = null;
|
||||||
canvasCtx: CanvasRenderingContext2D | null = null;
|
canvasCtx: CanvasRenderingContext2D | null = null;
|
||||||
lGraph: LGraph | null = null;
|
lGraph: LGraph | null = null;
|
||||||
lCanvas: LGraphCanvas | null = null;
|
lCanvas: ComfyGraphCanvas | null = null;
|
||||||
dropZone: HTMLElement | null = null;
|
dropZone: HTMLElement | null = null;
|
||||||
nodeOutputs: Record<string, any> = {};
|
nodeOutputs: Record<string, any> = {};
|
||||||
eventBus: TypedEmitter<ComfyAppEvents> = new EventEmitter() as TypedEmitter<ComfyAppEvents>;
|
eventBus: TypedEmitter<ComfyAppEvents> = new EventEmitter() as TypedEmitter<ComfyAppEvents>;
|
||||||
@@ -102,9 +104,8 @@ export default class ComfyApp {
|
|||||||
try {
|
try {
|
||||||
const json = localStorage.getItem("workflow");
|
const json = localStorage.getItem("workflow");
|
||||||
if (json) {
|
if (json) {
|
||||||
const workflow = JSON.parse(json) as SerializedAppState;
|
const state = JSON.parse(json) as SerializedAppState;
|
||||||
this.loadGraphData(workflow["workflow"]);
|
this.deserialize(state)
|
||||||
this.eventBus.emit("restored", workflow);
|
|
||||||
restored = true;
|
restored = true;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -113,11 +114,11 @@ export default class ComfyApp {
|
|||||||
|
|
||||||
// We failed to restore a workflow so load the default
|
// We failed to restore a workflow so load the default
|
||||||
if (!restored) {
|
if (!restored) {
|
||||||
this.loadGraphData();
|
this.initDefaultGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save current workflow automatically
|
// Save current workflow automatically
|
||||||
setInterval(this.requestAutosave.bind(this), 15000);
|
setInterval(this.saveStateToLocalStorage.bind(this), 1000);
|
||||||
|
|
||||||
this.addApiUpdateHandlers();
|
this.addApiUpdateHandlers();
|
||||||
this.addDropHandler();
|
this.addDropHandler();
|
||||||
@@ -181,8 +182,10 @@ export default class ComfyApp {
|
|||||||
this.eventBus.emit("cleared");
|
this.eventBus.emit("cleared");
|
||||||
}
|
}
|
||||||
|
|
||||||
private requestAutosave() {
|
saveStateToLocalStorage() {
|
||||||
this.eventBus.emit("autosave", this.lGraph);
|
const savedWorkflow = this.serialize();
|
||||||
|
const json = JSON.stringify(savedWorkflow);
|
||||||
|
localStorage.setItem("workflow", json)
|
||||||
}
|
}
|
||||||
|
|
||||||
private addGraphLifecycleHooks() {
|
private addGraphLifecycleHooks() {
|
||||||
@@ -305,20 +308,6 @@ export default class ComfyApp {
|
|||||||
// await this.#invokeExtensionsAsync("registerCustomNodes");
|
// await this.#invokeExtensionsAsync("registerCustomNodes");
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): SerializedAppState {
|
|
||||||
const graph = this.lGraph;
|
|
||||||
|
|
||||||
const serializedGraph = graph.serialize()
|
|
||||||
const serializedLayout = layoutState.serialize()
|
|
||||||
|
|
||||||
return {
|
|
||||||
createdBy: "ComfyBox",
|
|
||||||
version: 1,
|
|
||||||
workflow: serializedGraph,
|
|
||||||
layout: serializedLayout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private showDropZone() {
|
private showDropZone() {
|
||||||
if (this.dropZone)
|
if (this.dropZone)
|
||||||
this.dropZone.style.display = "block";
|
this.dropZone.style.display = "block";
|
||||||
@@ -365,24 +354,24 @@ export default class ComfyApp {
|
|||||||
* Adds a handler on paste that extracts and loads workflows from pasted JSON data
|
* Adds a handler on paste that extracts and loads workflows from pasted JSON data
|
||||||
*/
|
*/
|
||||||
private addPasteHandler() {
|
private addPasteHandler() {
|
||||||
document.addEventListener("paste", (e) => {
|
// document.addEventListener("paste", (e) => {
|
||||||
let data = (e.clipboardData || (window as any).clipboardData).getData("text/plain");
|
// let data = (e.clipboardData || (window as any).clipboardData).getData("text/plain");
|
||||||
let workflow;
|
// let workflow;
|
||||||
try {
|
// try {
|
||||||
data = data.slice(data.indexOf("{"));
|
// data = data.slice(data.indexOf("{"));
|
||||||
workflow = JSON.parse(data);
|
// workflow = JSON.parse(data);
|
||||||
} catch (err) {
|
// } catch (err) {
|
||||||
try {
|
// try {
|
||||||
data = data.slice(data.indexOf("workflow\n"));
|
// data = data.slice(data.indexOf("workflow\n"));
|
||||||
data = data.slice(data.indexOf("{"));
|
// data = data.slice(data.indexOf("{"));
|
||||||
workflow = JSON.parse(data);
|
// workflow = JSON.parse(data);
|
||||||
} catch (error) { }
|
// } catch (error) { }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (workflow && workflow.version && workflow.nodes && workflow.extra) {
|
// if (workflow && workflow.version && workflow.nodes && workflow.extra) {
|
||||||
this.loadGraphData(workflow);
|
// this.loadGraphData(workflow);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -436,17 +425,55 @@ export default class ComfyApp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serialize(): SerializedAppState {
|
||||||
|
const graph = this.lGraph;
|
||||||
|
|
||||||
|
const serializedGraph = graph.serialize()
|
||||||
|
const serializedLayout = layoutState.serialize()
|
||||||
|
const serializedCanvas = this.lCanvas.serialize();
|
||||||
|
|
||||||
|
return {
|
||||||
|
createdBy: "ComfyBox",
|
||||||
|
version: COMFYBOX_SERIAL_VERSION,
|
||||||
|
workflow: serializedGraph,
|
||||||
|
layout: serializedLayout,
|
||||||
|
canvas: serializedCanvas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize(data: SerializedAppState) {
|
||||||
|
if (data.version !== COMFYBOX_SERIAL_VERSION) {
|
||||||
|
throw `Invalid ComfyBox saved data format: ${data.version}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure loadGraphData does not trigger any state changes in layoutState
|
||||||
|
// (isConfiguring is set to true here)
|
||||||
|
// lGraph.configure will add new nodes, triggering onNodeAdded, but we
|
||||||
|
// want to restore the layoutState ourselves
|
||||||
|
layoutState.onStartConfigure();
|
||||||
|
|
||||||
|
this.loadGraphData(data.workflow)
|
||||||
|
|
||||||
|
// Now restore the layout
|
||||||
|
// Subsequent added nodes will add the UI data to layoutState
|
||||||
|
layoutState.deserialize(data.layout, this.lGraph)
|
||||||
|
|
||||||
|
// Restore canvas offset/zoom
|
||||||
|
this.lCanvas.deserialize(data.canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
initDefaultGraph() {
|
||||||
|
const state = structuredClone(defaultGraph)
|
||||||
|
this.deserialize(state)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the graph with the specified workflow data
|
* Populates the graph with the specified workflow data
|
||||||
* @param {*} graphData A serialized graph object
|
* @param {*} graphData A serialized graph object
|
||||||
*/
|
*/
|
||||||
loadGraphData(graphData?: SerializedLGraph) {
|
loadGraphData(graphData: SerializedLGraph) {
|
||||||
this.clean();
|
this.clean();
|
||||||
|
|
||||||
if (!graphData) {
|
|
||||||
graphData = structuredClone(defaultGraph.workflow)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
||||||
for (let n of graphData.nodes) {
|
for (let n of graphData.nodes) {
|
||||||
if (n.type == "T2IAdapterLoader") n.type = "ControlNetLoader";
|
if (n.type == "T2IAdapterLoader") n.type = "ControlNetLoader";
|
||||||
@@ -463,6 +490,25 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.clean();
|
||||||
|
|
||||||
|
const blankGraph: SerializedLGraph = {
|
||||||
|
last_node_id: 0,
|
||||||
|
last_link_id: 0,
|
||||||
|
nodes: [],
|
||||||
|
links: [],
|
||||||
|
groups: [],
|
||||||
|
config: {},
|
||||||
|
extra: {},
|
||||||
|
version: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutState.onStartConfigure();
|
||||||
|
this.lGraph.configure(blankGraph)
|
||||||
|
layoutState.initDefaultLayout();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the current graph workflow for sending to the API
|
* Converts the current graph workflow for sending to the API
|
||||||
* @returns The workflow and node links
|
* @returns The workflow and node links
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import ComfyApp from "./ComfyApp";
|
import ComfyApp from "./ComfyApp";
|
||||||
import type { SerializedPanes } from "./ComfyApp"
|
import type { SerializedPanes } from "./ComfyApp"
|
||||||
import WidgetContainer from "./WidgetContainer.svelte";
|
import WidgetContainer from "./WidgetContainer.svelte";
|
||||||
import layoutState, { type ContainerLayout, type DragItem } from "$lib/stores/layoutState";
|
import layoutState, { type ContainerLayout, type DragItem, type IDragItem } from "$lib/stores/layoutState";
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
|
|
||||||
import Menu from './menu/Menu.svelte';
|
import Menu from './menu/Menu.svelte';
|
||||||
@@ -15,18 +15,8 @@
|
|||||||
import Icon from './menu/Icon.svelte'
|
import Icon from './menu/Icon.svelte'
|
||||||
|
|
||||||
export let app: ComfyApp;
|
export let app: ComfyApp;
|
||||||
|
let root: IDragItem | null;
|
||||||
let dragConfigured: boolean = false;
|
let dragConfigured: boolean = false;
|
||||||
//
|
|
||||||
// function addUIForNewNode(node: LGraphNode, paneIndex?: number) {
|
|
||||||
// if (!paneIndex)
|
|
||||||
// paneIndex = findLeastPopulatedPaneIndex();
|
|
||||||
// dragItems[paneIndex].push({ id: totalId++, node: node });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $: if(app && !dragConfigured) {
|
|
||||||
// dragConfigured = true;
|
|
||||||
// app.eventBus.on("nodeAdded", addUIForNewNode);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Serialize UI panel order so it can be restored when workflow is loaded
|
* Serialize UI panel order so it can be restored when workflow is loaded
|
||||||
@@ -72,6 +62,12 @@
|
|||||||
|
|
||||||
$: $layoutState.isMenuOpen = showMenu;
|
$: $layoutState.isMenuOpen = showMenu;
|
||||||
|
|
||||||
|
$: if ($layoutState.root) {
|
||||||
|
root = $layoutState.root
|
||||||
|
} else {
|
||||||
|
root = null;
|
||||||
|
}
|
||||||
|
|
||||||
async function onRightClick(e) {
|
async function onRightClick(e) {
|
||||||
if ($uiState.uiEditMode === "disabled")
|
if ($uiState.uiEditMode === "disabled")
|
||||||
return;
|
return;
|
||||||
@@ -92,7 +88,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="comfy-ui-panes" on:contextmenu={onRightClick}>
|
<div id="comfy-ui-panes" on:contextmenu={onRightClick}>
|
||||||
<WidgetContainer bind:dragItem={$layoutState.root} classes={["root-container"]} />
|
<WidgetContainer bind:dragItem={root} classes={["root-container"]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showMenu}
|
{#if showMenu}
|
||||||
|
|||||||
@@ -94,15 +94,14 @@ type LayoutStateOps = {
|
|||||||
updateChildren: (parent: IDragItem, children: IDragItem[]) => IDragItem[],
|
updateChildren: (parent: IDragItem, children: IDragItem[]) => IDragItem[],
|
||||||
nodeAdded: (node: LGraphNode) => void,
|
nodeAdded: (node: LGraphNode) => void,
|
||||||
nodeRemoved: (node: LGraphNode) => void,
|
nodeRemoved: (node: LGraphNode) => void,
|
||||||
configureFinished: (graph: LGraph) => void,
|
|
||||||
groupItems: (dragItems: IDragItem[]) => ContainerLayout,
|
groupItems: (dragItems: IDragItem[]) => ContainerLayout,
|
||||||
ungroup: (container: ContainerLayout) => void,
|
ungroup: (container: ContainerLayout) => void,
|
||||||
getCurrentSelection: () => IDragItem[],
|
getCurrentSelection: () => IDragItem[],
|
||||||
findLayoutForNode: (nodeId: number) => IDragItem | null;
|
findLayoutForNode: (nodeId: number) => IDragItem | null;
|
||||||
clear: (state?: Partial<LayoutState>) => void,
|
|
||||||
serialize: () => SerializedLayoutState,
|
serialize: () => SerializedLayoutState,
|
||||||
deserialize: (data: SerializedLayoutState, graph: LGraph) => void,
|
deserialize: (data: SerializedLayoutState, graph: LGraph) => void,
|
||||||
resetLayout: () => void,
|
initDefaultLayout: () => void,
|
||||||
|
onStartConfigure: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WritableLayoutStateStore = Writable<LayoutState> & LayoutStateOps;
|
export type WritableLayoutStateStore = Writable<LayoutState> & LayoutStateOps;
|
||||||
@@ -114,7 +113,6 @@ const store: Writable<LayoutState> = writable({
|
|||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: true
|
isConfiguring: true
|
||||||
})
|
})
|
||||||
addContainer(null, { direction: "horizontal", showTitle: false });
|
|
||||||
|
|
||||||
function findDefaultContainerForInsertion(): ContainerLayout | null {
|
function findDefaultContainerForInsertion(): ContainerLayout | null {
|
||||||
const state = get(store);
|
const state = get(store);
|
||||||
@@ -154,6 +152,7 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes>
|
|||||||
if (parent) {
|
if (parent) {
|
||||||
moveItem(dragItem, parent)
|
moveItem(dragItem, parent)
|
||||||
}
|
}
|
||||||
|
console.debug("[layoutState] addContainer", state)
|
||||||
store.set(state)
|
store.set(state)
|
||||||
return dragItem;
|
return dragItem;
|
||||||
}
|
}
|
||||||
@@ -176,6 +175,7 @@ function addWidget(parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partia
|
|||||||
const parentEntry = state.allItems[parent.id]
|
const parentEntry = state.allItems[parent.id]
|
||||||
const entry: DragItemEntry = { dragItem, children: [], parent: null };
|
const entry: DragItemEntry = { dragItem, children: [], parent: null };
|
||||||
state.allItems[dragItem.id] = entry;
|
state.allItems[dragItem.id] = entry;
|
||||||
|
console.debug("[layoutState] addWidget", state)
|
||||||
moveItem(dragItem, parent)
|
moveItem(dragItem, parent)
|
||||||
return dragItem;
|
return dragItem;
|
||||||
}
|
}
|
||||||
@@ -250,28 +250,6 @@ function nodeRemoved(node: LGraphNode) {
|
|||||||
store.set(state)
|
store.set(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
function configureFinished(graph: LGraph) {
|
|
||||||
const id = 0;
|
|
||||||
|
|
||||||
clear({ isConfiguring: false })
|
|
||||||
|
|
||||||
const root = addContainer(null, { direction: "horizontal", showTitle: false });
|
|
||||||
const left = addContainer(root, { direction: "vertical", showTitle: false });
|
|
||||||
const right = addContainer(root, { direction: "vertical", showTitle: false });
|
|
||||||
|
|
||||||
const state = get(store)
|
|
||||||
state.root = root;
|
|
||||||
store.set(state)
|
|
||||||
|
|
||||||
console.debug("[layoutState] configure begin", state, graph)
|
|
||||||
|
|
||||||
for (const node of graph._nodes) {
|
|
||||||
nodeAdded(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.debug("[layoutState] configureFinished", state)
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveItem(target: IDragItem, to: ContainerLayout, index: number = -1) {
|
function moveItem(target: IDragItem, to: ContainerLayout, index: number = -1) {
|
||||||
const state = get(store)
|
const state = get(store)
|
||||||
const entry = state.allItems[target.id]
|
const entry = state.allItems[target.id]
|
||||||
@@ -375,20 +353,25 @@ function findLayoutForNode(nodeId: number): WidgetLayout | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear(state: Partial<LayoutState> = {}) {
|
function initDefaultLayout() {
|
||||||
store.set({
|
store.set({
|
||||||
root: null,
|
root: null,
|
||||||
allItems: {},
|
allItems: {},
|
||||||
currentId: 0,
|
currentId: 0,
|
||||||
currentSelection: [],
|
currentSelection: [],
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: true,
|
isConfiguring: false
|
||||||
...state
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
function resetLayout() {
|
const root = addContainer(null, { direction: "horizontal", showTitle: false });
|
||||||
// TODO
|
const left = addContainer(root, { direction: "vertical", showTitle: false });
|
||||||
|
const right = addContainer(root, { direction: "vertical", showTitle: false });
|
||||||
|
|
||||||
|
const state = get(store)
|
||||||
|
state.root = root;
|
||||||
|
store.set(state)
|
||||||
|
|
||||||
|
console.debug("[layoutState] initDefault", state)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SerializedLayoutState = {
|
export type SerializedLayoutState = {
|
||||||
@@ -436,8 +419,6 @@ function serialize(): SerializedLayoutState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deserialize(data: SerializedLayoutState, graph: LGraph) {
|
function deserialize(data: SerializedLayoutState, graph: LGraph) {
|
||||||
clear();
|
|
||||||
|
|
||||||
const allItems: Record<DragItemID, DragItemEntry> = {}
|
const allItems: Record<DragItemID, DragItemEntry> = {}
|
||||||
for (const pair of Object.entries(data.allItems)) {
|
for (const pair of Object.entries(data.allItems)) {
|
||||||
const [id, entry] = pair;
|
const [id, entry] = pair;
|
||||||
@@ -469,16 +450,28 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = null;
|
let root: IDragItem = null;
|
||||||
if (data.root)
|
if (data.root)
|
||||||
root = allItems[data.root]
|
root = allItems[data.root].dragItem
|
||||||
|
|
||||||
const state = get(store)
|
const state: LayoutState = {
|
||||||
store.set({
|
|
||||||
...state,
|
|
||||||
root,
|
root,
|
||||||
allItems,
|
allItems,
|
||||||
currentId: data.currentId,
|
currentId: data.currentId,
|
||||||
|
currentSelection: [],
|
||||||
|
isMenuOpen: false,
|
||||||
|
isConfiguring: false
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug("[layoutState] deserialize", data, state)
|
||||||
|
|
||||||
|
store.set(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStartConfigure() {
|
||||||
|
store.update(s => {
|
||||||
|
s.isConfiguring = true;
|
||||||
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,13 +484,12 @@ const layoutStateStore: WritableLayoutStateStore =
|
|||||||
updateChildren,
|
updateChildren,
|
||||||
nodeAdded,
|
nodeAdded,
|
||||||
nodeRemoved,
|
nodeRemoved,
|
||||||
configureFinished,
|
|
||||||
getCurrentSelection,
|
getCurrentSelection,
|
||||||
groupItems,
|
groupItems,
|
||||||
findLayoutForNode,
|
findLayoutForNode,
|
||||||
ungroup,
|
ungroup,
|
||||||
clear,
|
initDefaultLayout,
|
||||||
resetLayout,
|
onStartConfigure,
|
||||||
serialize,
|
serialize,
|
||||||
deserialize
|
deserialize
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,24 +16,11 @@
|
|||||||
|
|
||||||
let app: ComfyApp | null = null;
|
let app: ComfyApp | null = null;
|
||||||
|
|
||||||
let serializedPaneOrder = {};
|
|
||||||
|
|
||||||
function doAutosave(graph: LGraph): void {
|
|
||||||
const savedWorkflow = app.serialize();
|
|
||||||
localStorage.setItem("workflow", JSON.stringify(savedWorkflow))
|
|
||||||
}
|
|
||||||
|
|
||||||
function doRestore(workflow: SerializedAppState) {
|
|
||||||
serializedPaneOrder = workflow.panes;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (app)
|
if (app)
|
||||||
return
|
return
|
||||||
app = $uiState.app = new ComfyApp();
|
app = $uiState.app = new ComfyApp();
|
||||||
|
|
||||||
app.eventBus.on("autosave", doAutosave);
|
|
||||||
app.eventBus.on("restored", doRestore);
|
|
||||||
|
|
||||||
app.api.addEventListener("status", (ev: CustomEvent) => {
|
app.api.addEventListener("status", (ev: CustomEvent) => {
|
||||||
queueState.statusUpdated(ev.detail as ComfyAPIStatus);
|
queueState.statusUpdated(ev.detail as ComfyAPIStatus);
|
||||||
|
|||||||
Reference in New Issue
Block a user