diff --git a/package.json b/package.json index fd2c54b..eba5ec1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "build:css": "pollen -c gradio/js/theme/src/pollen.config.cjs && mv src/pollen.css node_modules/@gradio/theme/src" }, "devDependencies": { + "@zerodevx/svelte-toast": "^0.9.3", "eslint": "^8.37.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-svelte3": "^4.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f34575..2e77552 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,7 +63,7 @@ importers: version: 1.2.1 svelte-preprocess: 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: specifier: ^5.5.3 version: 5.5.3 @@ -72,7 +72,7 @@ importers: version: 0.7.13(svelte@3.58.0) tailwindcss: specifier: ^3.3.1 - version: 3.3.1(postcss@8.4.21) + version: 3.3.1 typed-emitter: specifier: github:andywer/typed-emitter version: github.com/andywer/typed-emitter/9a139b6fa0ec6b0db6141b5b756b784e4f7ef4e4 @@ -80,6 +80,9 @@ importers: specifier: ^1.0.5 version: 1.0.5(vite@4.3.1) devDependencies: + '@zerodevx/svelte-toast': + specifier: ^0.9.3 + version: 0.9.3(svelte@3.58.0) eslint: specifier: ^8.37.0 version: 8.37.0 @@ -103,7 +106,7 @@ importers: version: 3.58.0 svelte-check: 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: specifier: ^0.9.22 version: 0.9.22(svelte@3.58.0) @@ -112,7 +115,7 @@ importers: version: 5.0.3 vite: 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: specifier: ^4.0.8 version: 4.0.8(typescript@5.0.3)(vite@4.3.1) @@ -2035,7 +2038,7 @@ packages: magic-string: 0.30.0 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) transitivePeerDependencies: - supports-color @@ -2178,6 +2181,7 @@ packages: /@types/node@18.16.0: resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==} + dev: true /@types/node@8.10.66: resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==} @@ -2281,6 +2285,14 @@ packages: eslint-visitor-keys: 3.4.0 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): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -5911,7 +5923,7 @@ packages: - sugarss 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==} hasBin: true peerDependencies: @@ -5924,7 +5936,7 @@ packages: picocolors: 1.0.0 sade: 1.8.1 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 transitivePeerDependencies: - '@babel/core' @@ -6084,7 +6096,7 @@ packages: typescript: 5.0.3 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==} engines: {node: '>= 14.10.0'} requiresBuild: true @@ -6125,7 +6137,6 @@ packages: '@types/pug': 2.0.6 detect-indent: 6.1.0 magic-string: 0.27.0 - postcss: 8.4.21 sass: 1.61.0 sorcery: 0.11.0 strip-indent: 3.0.0 @@ -6190,6 +6201,39 @@ packages: get-port: 3.2.0 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): resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==} engines: {node: '>=12.13.0'} @@ -6223,6 +6267,7 @@ packages: sucrase: 3.32.0 transitivePeerDependencies: - ts-node + dev: true /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} @@ -6961,7 +7006,7 @@ packages: dependencies: picocolors: 1.0.0 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 /vite-tsconfig-paths@4.0.8(typescript@5.0.3)(vite@4.3.1): @@ -6975,7 +7020,7 @@ packages: debug: 4.3.4 globrex: 0.1.2 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: - supports-color - typescript @@ -7071,6 +7116,39 @@ packages: sass: 1.61.0 optionalDependencies: 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): resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} @@ -7080,7 +7158,7 @@ packages: vite: optional: true 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): resolution: {integrity: sha512-X75TApG2wZTJn299E/TIYevr4E9/nBo1sUtZzn0Ci5oK8qnpZAZyhwg0qCeMSakGIWtc6oRwcQFyFfW14aOFWg==} diff --git a/src/lib/ComfyGraphCanvas.ts b/src/lib/ComfyGraphCanvas.ts index 82b19ae..3a464bf 100644 --- a/src/lib/ComfyGraphCanvas.ts +++ b/src/lib/ComfyGraphCanvas.ts @@ -3,6 +3,11 @@ import type ComfyApp from "./components/ComfyApp"; import queueState from "./stores/queueState"; import { get } from "svelte/store"; +export type SerializedGraphCanvasState = { + offset: Vector2, + scale: number +} + export default class ComfyGraphCanvas extends LGraphCanvas { app: ComfyApp @@ -20,6 +25,23 @@ export default class ComfyGraphCanvas extends LGraphCanvas { 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( node: LGraphNode, ctx: CanvasRenderingContext2D, diff --git a/src/lib/components/ComfyApp.svelte b/src/lib/components/ComfyApp.svelte index d06466b..8391532 100644 --- a/src/lib/components/ComfyApp.svelte +++ b/src/lib/components/ComfyApp.svelte @@ -9,8 +9,8 @@ import uiState from "$lib/stores/uiState"; import layoutState from "$lib/stores/layoutState"; import { ImageViewer } from "$lib/ImageViewer"; - import { download } from "$lib/utils" import type { ComfyAPIStatus } from "$lib/api"; + import { SvelteToast, toast } from '@zerodevx/svelte-toast' import { LGraph } from "@litegraph-ts/core"; import LightboxModal from "./LightboxModal.svelte"; @@ -19,11 +19,20 @@ let app: ComfyApp = undefined; let imageViewer: ImageViewer; - let uiPane: ComfyUIPane = undefined; let queue: ComfyQueue = undefined; let mainElem: HTMLDivElement; + let uiPane: ComfyUIPane = undefined; 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) { 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 { if (!app?.lGraph) return; - const date = new Date(); - const formattedDate = date.toISOString().replace(/:/g, '-').replace(/\.\d{3}/g, '').replace('T', '_').replace("Z", ""); + app.saveStateToLocalStorage(); + 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 () => { app = new ComfyApp(); + if (debugLayout) { + layoutState.subscribe(s => { + console.warn("UPDATESTATE", s) + }) + } + app.eventBus.on("nodeAdded", layoutState.nodeAdded); 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) => { queueState.statusUpdated(ev.detail as ComfyAPIStatus); @@ -117,7 +132,7 @@ }) -
+
@@ -153,6 +168,12 @@ + + @@ -164,6 +185,8 @@
+ +