Copy action and button

This commit is contained in:
space-nuko
2023-05-04 16:13:46 -05:00
parent 705633d125
commit 18d27694fd
24 changed files with 700 additions and 383 deletions

View File

@@ -43,6 +43,7 @@
"@gradio/utils": "workspace:*", "@gradio/utils": "workspace:*",
"@litegraph-ts/core": "workspace:*", "@litegraph-ts/core": "workspace:*",
"@litegraph-ts/nodes-basic": "workspace:*", "@litegraph-ts/nodes-basic": "workspace:*",
"@litegraph-ts/nodes-events": "workspace:*",
"@litegraph-ts/tsconfig": "workspace:*", "@litegraph-ts/tsconfig": "workspace:*",
"@sveltejs/vite-plugin-svelte": "^2.1.1", "@sveltejs/vite-plugin-svelte": "^2.1.1",
"@tsconfig/svelte": "^4.0.1", "@tsconfig/svelte": "^4.0.1",

197
pnpm-lock.yaml generated
View File

@@ -37,6 +37,9 @@ importers:
'@litegraph-ts/nodes-basic': '@litegraph-ts/nodes-basic':
specifier: workspace:* specifier: workspace:*
version: link:litegraph/packages/nodes-basic version: link:litegraph/packages/nodes-basic
'@litegraph-ts/nodes-events':
specifier: workspace:*
version: link:litegraph/packages/nodes-events
'@litegraph-ts/tsconfig': '@litegraph-ts/tsconfig':
specifier: workspace:* specifier: workspace:*
version: link:litegraph/packages/tsconfig version: link:litegraph/packages/tsconfig
@@ -129,7 +132,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^2.9.9 specifier: ^2.9.9
version: 2.9.9(sass@1.61.0) version: 2.9.9
gradio/js/accordion: {} gradio/js/accordion: {}
@@ -694,7 +697,7 @@ importers:
version: 1.0.0-next.91(@sveltejs/kit@1.15.2) version: 1.0.0-next.91(@sveltejs/kit@1.15.2)
'@sveltejs/kit': '@sveltejs/kit':
specifier: ^1.0.0-next.318 specifier: ^1.0.0-next.318
version: 1.15.2(svelte@3.58.0)(vite@4.3.1) version: 1.15.2(svelte@3.58.0)
autoprefixer: autoprefixer:
specifier: ^10.4.2 specifier: ^10.4.2
version: 10.4.2(postcss@8.4.21) version: 10.4.2(postcss@8.4.21)
@@ -706,13 +709,13 @@ importers:
version: 3.1.1 version: 3.1.1
svelte-check: svelte-check:
specifier: ^2.2.6 specifier: ^2.2.6
version: 2.2.6(postcss-load-config@3.1.1)(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0) version: 2.2.6(postcss-load-config@3.1.1)(postcss@8.4.21)(svelte@3.58.0)
svelte-preprocess: svelte-preprocess:
specifier: ^4.10.1 specifier: ^4.10.1
version: 4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)(typescript@4.5.4) version: 4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(svelte@3.58.0)(typescript@4.5.4)
tailwindcss: tailwindcss:
specifier: ^3.0.12 specifier: ^3.0.12
version: 3.3.1(postcss@8.4.21) version: 3.3.1
tslib: tslib:
specifier: ^2.3.1 specifier: ^2.3.1
version: 2.3.1 version: 2.3.1
@@ -746,7 +749,7 @@ importers:
version: 5.0.3 version: 5.0.3
vite: vite:
specifier: ^4.2.1 specifier: ^4.2.1
version: 4.2.1(sass@1.61.0) version: 4.2.1
vite-plugin-checker: vite-plugin-checker:
specifier: ^0.5.6 specifier: ^0.5.6
version: 0.5.6(eslint@8.37.0)(typescript@5.0.3)(vite@4.2.1) version: 0.5.6(eslint@8.37.0)(typescript@5.0.3)(vite@4.2.1)
@@ -765,7 +768,23 @@ importers:
version: 5.0.3 version: 5.0.3
vite: vite:
specifier: ^4.2.1 specifier: ^4.2.1
version: 4.2.1(sass@1.61.0) version: 4.2.1
litegraph/packages/nodes-events:
dependencies:
'@litegraph-ts/core':
specifier: workspace:*
version: link:../core
devDependencies:
'@litegraph-ts/tsconfig':
specifier: workspace:*
version: link:../tsconfig
typescript:
specifier: ^5.0.3
version: 5.0.3
vite:
specifier: ^4.2.1
version: 4.3.1
litegraph/packages/tsconfig: {} litegraph/packages/tsconfig: {}
@@ -1993,11 +2012,11 @@ packages:
peerDependencies: peerDependencies:
'@sveltejs/kit': ^1.0.0-next.587 '@sveltejs/kit': ^1.0.0-next.587
dependencies: dependencies:
'@sveltejs/kit': 1.15.2(svelte@3.58.0)(vite@4.3.1) '@sveltejs/kit': 1.15.2(svelte@3.58.0)
import-meta-resolve: 2.2.2 import-meta-resolve: 2.2.2
dev: true dev: true
/@sveltejs/kit@1.15.2(svelte@3.58.0)(vite@4.3.1): /@sveltejs/kit@1.15.2(svelte@3.58.0):
resolution: {integrity: sha512-rLNxZrjbrlPf8AWW8GAU4L/Vvu17e9v8EYl7pUip7x72lTft7RcxeP3z7tsrHpMSBBxC9o4XdKzFvz1vMZyXZw==} resolution: {integrity: sha512-rLNxZrjbrlPf8AWW8GAU4L/Vvu17e9v8EYl7pUip7x72lTft7RcxeP3z7tsrHpMSBBxC9o4XdKzFvz1vMZyXZw==}
engines: {node: ^16.14 || >=18} engines: {node: ^16.14 || >=18}
hasBin: true hasBin: true
@@ -2006,7 +2025,7 @@ packages:
svelte: ^3.54.0 svelte: ^3.54.0
vite: ^4.0.0 vite: ^4.0.0
dependencies: dependencies:
'@sveltejs/vite-plugin-svelte': 2.1.1(svelte@3.58.0)(vite@4.3.1) '@sveltejs/vite-plugin-svelte': 2.1.1(svelte@3.58.0)
'@types/cookie': 0.5.1 '@types/cookie': 0.5.1
cookie: 0.5.0 cookie: 0.5.0
devalue: 4.3.0 devalue: 4.3.0
@@ -2020,7 +2039,24 @@ packages:
svelte: 3.58.0 svelte: 3.58.0
tiny-glob: 0.2.9 tiny-glob: 0.2.9
undici: 5.20.0 undici: 5.20.0
vite: 4.3.1(@types/node@18.16.0)(sass@1.61.0) transitivePeerDependencies:
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte@2.1.1(svelte@3.58.0):
resolution: {integrity: sha512-7YeBDt4us0FiIMNsVXxyaP4Hwyn2/v9x3oqStkHU3ZdIc5O22pGwUwH33wUqYo+7Itdmo8zxJ45Qvfm3H7UUjQ==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
svelte: ^3.54.0
vite: ^4.0.0
dependencies:
debug: 4.3.4
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.0
svelte: 3.58.0
svelte-hmr: 0.15.1(svelte@3.58.0)
vitefu: 0.2.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@@ -2042,6 +2078,7 @@ packages:
vitefu: 0.2.4(vite@4.3.1) vitefu: 0.2.4(vite@4.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: false
/@ts-morph/common@0.18.1: /@ts-morph/common@0.18.1:
resolution: {integrity: sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA==} resolution: {integrity: sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA==}
@@ -4304,7 +4341,7 @@ packages:
exit: 0.1.2 exit: 0.1.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
import-local: 3.1.0 import-local: 3.1.0
jest-config: 29.5.0(@types/node@18.16.0) jest-config: 29.5.0
jest-util: 29.5.0 jest-util: 29.5.0
jest-validate: 29.5.0 jest-validate: 29.5.0
prompts: 2.4.2 prompts: 2.4.2
@@ -4315,6 +4352,44 @@ packages:
- ts-node - ts-node
dev: true dev: true
/jest-config@29.5.0:
resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@types/node': '*'
ts-node: '>=9.0.0'
peerDependenciesMeta:
'@types/node':
optional: true
ts-node:
optional: true
dependencies:
'@babel/core': 7.21.4
'@jest/test-sequencer': 29.5.0
'@jest/types': 29.5.0
babel-jest: 29.5.0(@babel/core@7.21.4)
chalk: 4.1.2
ci-info: 3.8.0
deepmerge: 4.3.1
glob: 7.2.3
graceful-fs: 4.2.11
jest-circus: 29.5.0
jest-environment-node: 29.5.0
jest-get-type: 29.4.3
jest-regex-util: 29.4.3
jest-resolve: 29.5.0
jest-runner: 29.5.0
jest-util: 29.5.0
jest-validate: 29.5.0
micromatch: 4.0.5
parse-json: 5.2.0
pretty-format: 29.5.0
slash: 3.0.0
strip-json-comments: 3.1.1
transitivePeerDependencies:
- supports-color
dev: true
/jest-config@29.5.0(@types/node@18.16.0): /jest-config@29.5.0(@types/node@18.16.0):
resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -5894,7 +5969,7 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
/svelte-check@2.2.6(postcss-load-config@3.1.1)(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0): /svelte-check@2.2.6(postcss-load-config@3.1.1)(postcss@8.4.21)(svelte@3.58.0):
resolution: {integrity: sha512-oJux/afbmcZO+N+ADXB88h6XANLie8Y2rh2qBlhgfkpr2c3t/q/T0w2JWrHqagaDL8zeNwO8a8RVFBkrRox8gg==} resolution: {integrity: sha512-oJux/afbmcZO+N+ADXB88h6XANLie8Y2rh2qBlhgfkpr2c3t/q/T0w2JWrHqagaDL8zeNwO8a8RVFBkrRox8gg==}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -5908,7 +5983,7 @@ packages:
sade: 1.8.1 sade: 1.8.1
source-map: 0.7.4 source-map: 0.7.4
svelte: 3.58.0 svelte: 3.58.0
svelte-preprocess: 4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3) svelte-preprocess: 4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(svelte@3.58.0)(typescript@5.0.3)
typescript: 5.0.3 typescript: 5.0.3
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
@@ -5988,7 +6063,7 @@ packages:
tiny-glob: 0.2.9 tiny-glob: 0.2.9
dev: false dev: false
/svelte-preprocess@4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)(typescript@4.5.4): /svelte-preprocess@4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(svelte@3.58.0)(typescript@4.5.4):
resolution: {integrity: sha512-NSNloaylf+o9UeyUR2KvpdxrAyMdHl3U7rMnoP06/sG0iwJvlUM4TpMno13RaNqovh4AAoGsx1jeYcIyuGUXMw==} resolution: {integrity: sha512-NSNloaylf+o9UeyUR2KvpdxrAyMdHl3U7rMnoP06/sG0iwJvlUM4TpMno13RaNqovh4AAoGsx1jeYcIyuGUXMw==}
engines: {node: '>= 9.11.2'} engines: {node: '>= 9.11.2'}
requiresBuild: true requiresBuild: true
@@ -6035,14 +6110,13 @@ packages:
magic-string: 0.25.9 magic-string: 0.25.9
postcss: 8.4.21 postcss: 8.4.21
postcss-load-config: 3.1.1 postcss-load-config: 3.1.1
sass: 1.61.0
sorcery: 0.10.0 sorcery: 0.10.0
strip-indent: 3.0.0 strip-indent: 3.0.0
svelte: 3.58.0 svelte: 3.58.0
typescript: 4.5.4 typescript: 4.5.4
dev: true dev: true
/svelte-preprocess@4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(sass@1.61.0)(svelte@3.58.0)(typescript@5.0.3): /svelte-preprocess@4.10.1(postcss-load-config@3.1.1)(postcss@8.4.21)(svelte@3.58.0)(typescript@5.0.3):
resolution: {integrity: sha512-NSNloaylf+o9UeyUR2KvpdxrAyMdHl3U7rMnoP06/sG0iwJvlUM4TpMno13RaNqovh4AAoGsx1jeYcIyuGUXMw==} resolution: {integrity: sha512-NSNloaylf+o9UeyUR2KvpdxrAyMdHl3U7rMnoP06/sG0iwJvlUM4TpMno13RaNqovh4AAoGsx1jeYcIyuGUXMw==}
engines: {node: '>= 9.11.2'} engines: {node: '>= 9.11.2'}
requiresBuild: true requiresBuild: true
@@ -6089,7 +6163,6 @@ packages:
magic-string: 0.25.9 magic-string: 0.25.9
postcss: 8.4.21 postcss: 8.4.21
postcss-load-config: 3.1.1 postcss-load-config: 3.1.1
sass: 1.61.0
sorcery: 0.10.0 sorcery: 0.10.0
strip-indent: 3.0.0 strip-indent: 3.0.0
svelte: 3.58.0 svelte: 3.58.0
@@ -6232,42 +6305,6 @@ packages:
sucrase: 3.32.0 sucrase: 3.32.0
transitivePeerDependencies: transitivePeerDependencies:
- ts-node - ts-node
dev: false
/tailwindcss@3.3.1(postcss@8.4.21):
resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==}
engines: {node: '>=12.13.0'}
hasBin: true
peerDependencies:
postcss: ^8.0.9
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: 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==}
@@ -6969,7 +7006,7 @@ packages:
strip-ansi: 6.0.1 strip-ansi: 6.0.1
tiny-invariant: 1.3.1 tiny-invariant: 1.3.1
typescript: 5.0.3 typescript: 5.0.3
vite: 4.2.1(sass@1.61.0) vite: 4.2.1
vscode-languageclient: 7.0.0 vscode-languageclient: 7.0.0
vscode-languageserver: 7.0.0 vscode-languageserver: 7.0.0
vscode-languageserver-textdocument: 1.0.8 vscode-languageserver-textdocument: 1.0.8
@@ -6992,7 +7029,7 @@ packages:
kolorist: 1.8.0 kolorist: 1.8.0
magic-string: 0.29.0 magic-string: 0.29.0
ts-morph: 17.0.1 ts-morph: 17.0.1
vite: 4.2.1(sass@1.61.0) vite: 4.2.1
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- rollup - rollup
@@ -7026,7 +7063,7 @@ packages:
- typescript - typescript
dev: true dev: true
/vite@2.9.9(sass@1.61.0): /vite@2.9.9:
resolution: {integrity: sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==} resolution: {integrity: sha512-ffaam+NgHfbEmfw/Vuh6BHKKlI/XIAhxE5QSS7gFLIngxg171mg1P3a4LSRME0z2ZU1ScxoKzphkipcYwSD5Ew==}
engines: {node: '>=12.2.0'} engines: {node: '>=12.2.0'}
hasBin: true hasBin: true
@@ -7046,12 +7083,11 @@ packages:
postcss: 8.4.21 postcss: 8.4.21
resolve: 1.22.2 resolve: 1.22.2
rollup: 2.79.1 rollup: 2.79.1
sass: 1.61.0
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
dev: true dev: true
/vite@4.2.1(sass@1.61.0): /vite@4.2.1:
resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==} resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true hasBin: true
@@ -7080,10 +7116,41 @@ packages:
postcss: 8.4.21 postcss: 8.4.21
resolve: 1.22.2 resolve: 1.22.2
rollup: 3.21.0 rollup: 3.21.0
sass: 1.61.0
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
/vite@4.3.1:
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
optionalDependencies:
fsevents: 2.3.2
dev: true
/vite@4.3.1(@types/node@18.16.0)(sass@1.61.0): /vite@4.3.1(@types/node@18.16.0)(sass@1.61.0):
resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==} resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@@ -7150,6 +7217,15 @@ packages:
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
/vitefu@0.2.4:
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
peerDependencies:
vite: ^3.0.0 || ^4.0.0
peerDependenciesMeta:
vite:
optional: true
dev: true
/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==}
peerDependencies: peerDependencies:
@@ -7159,6 +7235,7 @@ packages:
optional: true optional: true
dependencies: dependencies:
vite: 4.3.1(sass@1.61.0) vite: 4.3.1(sass@1.61.0)
dev: false
/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==}

View File

@@ -1,6 +1,4 @@
packages: packages:
- 'gradio/js/*' - 'gradio/js/*'
- 'gradio/client/js' - 'gradio/client/js'
- 'litegraph/packages/core' - 'litegraph/packages/*'
- 'litegraph/packages/nodes-basic'
- 'litegraph/packages/tsconfig'

View File

@@ -1,12 +1,15 @@
<script lang="ts"> <script lang="ts">
import ComfyApp from "$lib/components/ComfyApp.svelte" import ComfyApp from "$lib/components/ComfyApp.svelte"
import { default as ComfyAppState } from "$lib/components/ComfyApp"
import "@litegraph-ts/core/css/litegraph.css"; import "@litegraph-ts/core/css/litegraph.css";
import "./scss/global.scss"; import "./scss/global.scss";
import { onMount } from 'svelte'; import { onMount } from 'svelte';
const app = new ComfyAppState();
</script> </script>
<ComfyApp/> <ComfyApp {app}/>
<style> <style>
</style> </style>

View File

@@ -7,6 +7,7 @@ import uiState from "./stores/uiState";
import { get } from "svelte/store"; import { get } from "svelte/store";
import type ComfyGraphNode from "./nodes/ComfyGraphNode"; import type ComfyGraphNode from "./nodes/ComfyGraphNode";
import type IComfyInputSlot from "./IComfyInputSlot"; import type IComfyInputSlot from "./IComfyInputSlot";
import type { ComfyBackendNode } from "./nodes/ComfyBackendNode";
type ComfyGraphEvents = { type ComfyGraphEvents = {
configured: (graph: LGraph) => void configured: (graph: LGraph) => void
@@ -16,6 +17,7 @@ type ComfyGraphEvents = {
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
afterExecute: () => void
} }
export default class ComfyGraph extends LGraph { export default class ComfyGraph extends LGraph {
@@ -42,6 +44,10 @@ export default class ComfyGraph extends LGraph {
this.eventBus.emit("afterChange", graph, info); this.eventBus.emit("afterChange", graph, info);
} }
override onAfterExecute() {
this.eventBus.emit("afterExecute");
}
override onNodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) { override onNodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) {
layoutState.nodeAdded(node) layoutState.nodeAdded(node)
this.graphSync.onNodeAdded(node); this.graphSync.onNodeAdded(node);
@@ -51,7 +57,7 @@ export default class ComfyGraph extends LGraph {
&& !options.addedByDeserialize // ...and we're not trying to deserialize an existing workflow && !options.addedByDeserialize // ...and we're not trying to deserialize an existing workflow
&& get(uiState).autoAddUI) { && get(uiState).autoAddUI) {
console.debug("[ComfyGraph] AutoAdd UI") console.debug("[ComfyGraph] AutoAdd UI")
const comfyNode = node as ComfyGraphNode; const comfyNode = node as ComfyBackendNode;
const widgetNodesAdded = [] const widgetNodesAdded = []
for (let index = 0; index < comfyNode.inputs.length; index++) { for (let index = 0; index < comfyNode.inputs.length; index++) {
const input = comfyNode.inputs[index]; const input = comfyNode.inputs[index];
@@ -70,7 +76,7 @@ export default class ComfyGraph extends LGraph {
} }
const dragItems = widgetNodesAdded.map(wn => get(layoutState).allItemsByNode[wn.id]?.dragItem).filter(di => di) const dragItems = widgetNodesAdded.map(wn => get(layoutState).allItemsByNode[wn.id]?.dragItem).filter(di => di)
console.debug("[ComfyGraph] Group new widgets", dragItems) console.debug("[ComfyGraph] Group new widgets", dragItems)
layoutState.groupItems(dragItems, comfyNode.comfyClass) layoutState.groupItems(dragItems, { title: comfyNode.comfyClass })
} }
console.debug("Added", node); console.debug("Added", node);

View File

@@ -143,7 +143,7 @@
gap: var(--layout-gap); gap: var(--layout-gap);
width: var(--size-full); width: var(--size-full);
.v-pane { > :global(.block > .v-pane) {
flex-direction: row; flex-direction: row;
} }
@@ -157,7 +157,7 @@
&.vertical { &.vertical {
position: relative; position: relative;
.v-pane { > :global(.block > .v-pane) {
flex-direction: column; flex-direction: column;
} }

View File

@@ -17,13 +17,14 @@
import ComfyQueue from "./ComfyQueue.svelte"; import ComfyQueue from "./ComfyQueue.svelte";
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
let app: ComfyApp = undefined; export let app: ComfyApp = undefined;
let imageViewer: ImageViewer; let imageViewer: ImageViewer;
let queue: ComfyQueue = undefined; let queue: ComfyQueue = undefined;
let mainElem: HTMLDivElement; let mainElem: HTMLDivElement;
let uiPane: ComfyUIPane = undefined; let uiPane: ComfyUIPane = undefined;
let containerElem: HTMLDivElement; let containerElem: HTMLDivElement;
let resizeTimeout: NodeJS.Timeout | null; let resizeTimeout: NodeJS.Timeout | null;
let hasShownUIHelpToast: boolean = false;
let debugLayout: boolean = true; let debugLayout: boolean = true;
@@ -44,8 +45,8 @@
app.queuePrompt(0, 1); app.queuePrompt(0, 1);
} }
$: if (app) app.lCanvas.allow_dragnodes = !$uiState.nodesLocked; $: if (app?.lCanvas) app.lCanvas.allow_dragnodes = !$uiState.nodesLocked;
$: if (app) app.lCanvas.allow_interaction = !$uiState.graphLocked; $: if (app?.lCanvas) app.lCanvas.allow_interaction = !$uiState.graphLocked;
$: if ($uiState.uiEditMode) $: if ($uiState.uiEditMode)
$layoutState.currentSelection = [] $layoutState.currentSelection = []
@@ -98,27 +99,26 @@
app.lCanvas.recenter(); app.lCanvas.recenter();
} }
onMount(async () => { $: if ($uiState.uiEditMode !== "disabled" && !hasShownUIHelpToast) {
app = new ComfyApp(); hasShownUIHelpToast = true;
toast.push("Right-click to open context menu.")
}
if (debugLayout) { if (debugLayout) {
layoutState.subscribe(s => { layoutState.subscribe(s => {
console.warn("UPDATESTATE", s) console.warn("UPDATESTATE", s)
}) })
} }
app.api.addEventListener("status", (ev: CustomEvent) => { app.api.addEventListener("status", (ev: CustomEvent) => {
queueState.statusUpdated(ev.detail as ComfyAPIStatus); queueState.statusUpdated(ev.detail as ComfyAPIStatus);
}); });
await app.setup();
(window as any).app = app;
(window as any).appPane = uiPane;
refreshView();
$: if (app.rootEl && !imageViewer) {
imageViewer = new ImageViewer(app.rootEl); imageViewer = new ImageViewer(app.rootEl);
}
$: if (containerElem) {
let wrappers = containerElem.querySelectorAll<HTMLDivElement>(".pane-wrapper") let wrappers = containerElem.querySelectorAll<HTMLDivElement>(".pane-wrapper")
for (const wrapper of wrappers) { for (const wrapper of wrappers) {
const paneNode = wrapper.parentNode as HTMLElement; // get the node inside the <Pane/> const paneNode = wrapper.parentNode as HTMLElement; // get the node inside the <Pane/>
@@ -126,6 +126,14 @@
app.resizeCanvas() app.resizeCanvas()
} }
} }
}
onMount(async () => {
await app.setup();
(window as any).app = app;
(window as any).appPane = uiPane;
refreshView();
}) })
</script> </script>

View File

@@ -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 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"
@@ -9,7 +9,9 @@ import type TypedEmitter from "typed-emitter";
// Import nodes // Import nodes
import "@litegraph-ts/nodes-basic" import "@litegraph-ts/nodes-basic"
import "@litegraph-ts/nodes-events"
import * as nodes from "$lib/nodes/index" import * as nodes from "$lib/nodes/index"
import ComfyGraphCanvas, { type SerializedGraphCanvasState } 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"
@@ -20,6 +22,7 @@ import type { SerializedLayoutState } from "$lib/stores/layoutState";
import layoutState from "$lib/stores/layoutState"; import layoutState from "$lib/stores/layoutState";
import { toast } from '@zerodevx/svelte-toast' import { toast } from '@zerodevx/svelte-toast'
import ComfyGraph from "$lib/ComfyGraph"; import ComfyGraph from "$lib/ComfyGraph";
import { ComfyBackendNode } from "$lib/nodes/ComfyBackendNode";
export const COMFYBOX_SERIAL_VERSION = 1; export const COMFYBOX_SERIAL_VERSION = 1;
@@ -61,12 +64,18 @@ export default class ComfyApp {
private queueItems: QueueItem[] = []; private queueItems: QueueItem[] = [];
private processingQueue: boolean = false; private processingQueue: boolean = false;
private alreadySetup = false;
constructor() { constructor() {
this.api = new ComfyAPI(); this.api = new ComfyAPI();
} }
async setup(): Promise<void> { async setup(): Promise<void> {
if (this.alreadySetup) {
console.error("Already setup")
return;
}
this.rootEl = document.getElementById("main") as HTMLDivElement; this.rootEl = document.getElementById("main") as HTMLDivElement;
this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement; this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement;
this.lGraph = new ComfyGraph(); this.lGraph = new ComfyGraph();
@@ -76,11 +85,7 @@ export default class ComfyApp {
LiteGraph.release_link_on_empty_shows_menu = true; LiteGraph.release_link_on_empty_shows_menu = true;
LiteGraph.alt_drag_do_clone_nodes = true; LiteGraph.alt_drag_do_clone_nodes = true;
this.lGraph.start();
// await this.#invokeExtensionsAsync("init"); // await this.#invokeExtensionsAsync("init");
this.registerNodeTypeOverrides();
this.registerWidgetTypeOverrides();
await this.registerNodes(); await this.registerNodes();
// Load previous workflow // Load previous workflow
@@ -109,10 +114,7 @@ export default class ComfyApp {
this.addPasteHandler(); this.addPasteHandler();
this.addKeyboardHandler(); this.addKeyboardHandler();
// Distinguish frontend/backend connections this.setupColorScheme()
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
// await this.#invokeExtensionsAsync("setup"); // await this.#invokeExtensionsAsync("setup");
@@ -120,6 +122,11 @@ export default class ComfyApp {
this.resizeCanvas(); this.resizeCanvas();
window.addEventListener("resize", this.resizeCanvas.bind(this)); 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(); return Promise.resolve();
} }
@@ -137,32 +144,14 @@ export default class ComfyApp {
localStorage.setItem("workflow", json) 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> = {} 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() { private async registerNodes() {
const app = this; const app = this;
// Load node definitions from the backend // Load node definitions from the backend
const defs = await this.api.getNodeDefs(); 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 // Register a node for each definition
for (const nodeId in defs) { for (const nodeId in defs) {
@@ -171,60 +160,11 @@ export default class ComfyApp {
const typeOverride = ComfyApp.node_type_overrides[nodeId] const typeOverride = ComfyApp.node_type_overrides[nodeId]
if (typeOverride) if (typeOverride)
console.debug("Attaching custom type to received node:", nodeId, 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 { const ctor = class extends baseClass {
constructor(title?: string) { constructor(title?: string) {
super(title); super(title, nodeId, nodeData);
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;
} }
} }
@@ -235,15 +175,9 @@ export default class ComfyApp {
desc: `ComfyNode: ${nodeId}` desc: `ComfyNode: ${nodeId}`
} }
// this.#addNodeContextMenuHandler(node);
// this.#addDrawBackgroundHandler(node, app);
// await this.#invokeExtensionsAsync("beforeRegisterNodeDef", node, nodeData);
LiteGraph.registerNodeType(node); LiteGraph.registerNodeType(node);
node.category = nodeData.category; node.category = nodeData.category;
} }
// await this.#invokeExtensionsAsync("registerCustomNodes");
} }
private showDropZone() { 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 { serialize(): SerializedAppState {
const graph = this.lGraph; const graph = this.lGraph;

View File

@@ -29,10 +29,10 @@
// TODO // TODO
} }
function groupWidgets() { function groupWidgets(horizontal: boolean) {
const items = layoutState.getCurrentSelection() const items = layoutState.getCurrentSelection()
$layoutState.currentSelection = [] $layoutState.currentSelection = []
layoutState.groupItems(items) layoutState.groupItems(items, { direction: horizontal ? "horizontal" : "vertical" })
} }
let canUngroup = false; let canUngroup = false;
@@ -95,8 +95,12 @@
<Menu {...menuPos} on:click={closeMenu} on:clickoutside={closeMenu}> <Menu {...menuPos} on:click={closeMenu} on:clickoutside={closeMenu}>
<MenuOption <MenuOption
isDisabled={$layoutState.currentSelection.length === 0} isDisabled={$layoutState.currentSelection.length === 0}
on:click={groupWidgets} on:click={() => groupWidgets(false)}
text="Group" /> text="Group" />
<MenuOption
isDisabled={$layoutState.currentSelection.length === 0}
on:click={() => groupWidgets(true)}
text="Group Horizontally" />
<MenuOption <MenuOption
isDisabled={!canUngroup} isDisabled={!canUngroup}
on:click={ungroup} on:click={ungroup}

View File

@@ -56,6 +56,9 @@
.widget.selected { .widget.selected {
background: var(--color-yellow-200); background: var(--color-yellow-200);
} }
.container.selected {
background: var(--color-yellow-400);
}
.is-executing { .is-executing {
border: 3px dashed var(--color-green-600) !important; border: 3px dashed var(--color-green-600) !important;

View File

@@ -0,0 +1,53 @@
import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, BuiltInSlotType, type ITextWidget } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode";
import { Watch } from "@litegraph-ts/nodes-basic";
export interface ComfyCopyActionProperties extends Record<any, any> {
value: any
}
export class ComfyCopyAction extends ComfyGraphNode {
override properties: ComfyCopyActionProperties = {
value: null
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "in", type: "*" },
{ name: "copy", type: BuiltInSlotType.ACTION }
],
outputs: [
{ name: "out", type: "*" }
],
}
displayWidget: ITextWidget;
constructor(title?: string) {
super(title);
this.displayWidget = this.addWidget<ITextWidget>(
"text",
"Value",
"",
"value"
);
this.displayWidget.disabled = true;
}
override onExecute() {
this.setProperty("value", this.getInputData(0))
}
override onAction(action: any, param: any) {
this.setProperty("value", this.getInputData(0))
this.setOutputData(0, this.properties.value)
console.log("setData", this.properties.value)
};
}
LiteGraph.registerNodeType({
class: ComfyCopyAction,
title: "Comfy.CopyAction",
desc: "Copies its input to its output when an event is received",
type: "actions/copy"
})

View File

@@ -0,0 +1,92 @@
import LGraphCanvas from "@litegraph-ts/core/src/LGraphCanvas";
import ComfyGraphNode from "./ComfyGraphNode";
import ComfyWidgets from "$lib/widgets"
import type { ComfyWidgetNode } from "./ComfyWidgetNodes";
/*
* Base class for any node with configuration sent by the backend.
*/
export class ComfyBackendNode extends ComfyGraphNode {
comfyClass: string;
constructor(title: string, comfyClass: string, nodeData: any) {
super(title)
this.type = comfyClass; // XXX: workaround dependency in LGraphNode.addInput()
this.comfyClass = comfyClass;
this.isBackendNode = true;
const color = LGraphCanvas.node_colors["yellow"];
this.color = color.color
this.bgColor = color.bgColor
this.setup(nodeData)
// ComfyUI has no obvious way to identify if a node will return outputs back to the frontend based on its properties.
// It just returns a hash like { "ui": { "images": results } } internally.
// So this will need to be hardcoded for now.
if (["PreviewImage", "SaveImage"].indexOf(comfyClass) !== -1) {
this.addOutput("output", "OUTPUT");
}
}
private setup(nodeData: any) {
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, ComfyWidgets.COMBO(this, inputName, inputData) || {});
} else if (`${type}:${inputName}` in ComfyWidgets) {
// Support custom ComfyWidgets by Type:Name
Object.assign(config, ComfyWidgets[`${type}:${inputName}`](this, inputName, inputData) || {});
} else if (type in ComfyWidgets) {
// Standard type ComfyWidgets
Object.assign(config, ComfyWidgets[type](this, inputName, inputData) || {});
} else {
// Node connection inputs (backend)
this.addInput(inputName, type);
}
}
}
for (const o in nodeData["output"]) {
const output = nodeData["output"][o];
const outputName = nodeData["output_name"][o] || output;
this.addOutput(outputName, output);
}
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);
}
override onExecuted(outputData: any) {
console.warn("onExecuted outputs", outputData)
for (let index = 0; index < this.outputs.length; index++) {
const output = this.outputs[index]
if (output.type === "OUTPUT") {
this.setOutputData(index, outputData)
for (const node of this.getOutputNodes(index)) {
if ("receiveOutput" in node) {
const widgetNode = node as ComfyWidgetNode;
widgetNode.receiveOutput();
}
}
}
}
}
}

View File

@@ -2,7 +2,6 @@ import type ComfyWidget from "$lib/components/widgets/ComfyWidget";
import { LGraph, LGraphNode } from "@litegraph-ts/core"; import { LGraph, LGraphNode } from "@litegraph-ts/core";
export default class ComfyGraphNode extends LGraphNode { export default class ComfyGraphNode extends LGraphNode {
comfyClass: string | null
isBackendNode?: boolean; isBackendNode?: boolean;
afterQueued?(): void; afterQueued?(): void;

View File

@@ -1,43 +0,0 @@
import ComfyGalleryWidget, { type ComfyGalleryEntry } from "$lib/widgets/ComfyGalleryWidget";
import ComfyGraphNode from "./ComfyGraphNode";
export type ComfyImageResult = {
filename: string,
subfolder: string,
type: "output" | "temp"
}
export type ComfyImageExecOutput = {
images: ComfyImageResult[]
}
/*
* Node with a single extra image output widget
*/
class ComfyImageNode extends ComfyGraphNode {
private _imageResults: Array<ComfyImageResult> = [];
private _galleryWidget: ComfyGalleryWidget;
constructor(title?: any) {
super(title)
this._galleryWidget = new ComfyGalleryWidget("Images", [], this);
this.addCustomWidget(this._galleryWidget);
}
override onExecuted(output: ComfyImageExecOutput) {
this._imageResults = Array.from(output.images); // TODO append?
const galleryItems = this._imageResults.map(r => {
// TODO
const url = "http://localhost:8188/view?"
const params = new URLSearchParams(r)
let entry: ComfyGalleryEntry = [url + params, null]
return entry
});
this._galleryWidget.addImages(galleryItems);
}
}
export class ComfySaveImageNode extends ComfyImageNode {
}
export class ComfyPreviewImageNode extends ComfyImageNode {
}

View File

@@ -1,15 +1,18 @@
import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, LGraph, type INodeInputSlot, type ITextWidget, type INodeOutputSlot, type SerializedLGraphNode } from "@litegraph-ts/core"; import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, LGraph, type INodeInputSlot, type ITextWidget, type INodeOutputSlot, type SerializedLGraphNode, BuiltInSlotType } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode"; import ComfyGraphNode from "./ComfyGraphNode";
import ComboWidget from "$lib/widgets/ComboWidget.svelte"; import ComboWidget from "$lib/widgets/ComboWidget.svelte";
import RangeWidget from "$lib/widgets/RangeWidget.svelte"; import RangeWidget from "$lib/widgets/RangeWidget.svelte";
import TextWidget from "$lib/widgets/TextWidget.svelte"; import TextWidget from "$lib/widgets/TextWidget.svelte";
import GalleryWidget from "$lib/widgets/GalleryWidget.svelte";
import ButtonWidget from "$lib/widgets/ButtonWidget.svelte";
import type { SvelteComponentDev } from "svelte/internal"; import type { SvelteComponentDev } from "svelte/internal";
import { ComfyWidgets } from "$lib/widgets";
import { Watch } from "@litegraph-ts/nodes-basic"; import { Watch } from "@litegraph-ts/nodes-basic";
import type IComfyInputSlot from "$lib/IComfyInputSlot"; import type IComfyInputSlot from "$lib/IComfyInputSlot";
import { writable, type Unsubscriber, type Writable, get } from "svelte/store"; import { writable, type Unsubscriber, type Writable, get } from "svelte/store";
import { clamp } from "$lib/utils" import { clamp } from "$lib/utils"
import layoutState from "$lib/stores/layoutState"; import layoutState from "$lib/stores/layoutState";
import type { FileData as GradioFileData } from "@gradio/upload";
import queueState from "$lib/stores/queueState";
export interface ComfyWidgetProperties extends Record<string, any> { export interface ComfyWidgetProperties extends Record<string, any> {
defaultValue: any defaultValue: any
@@ -36,6 +39,10 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
override isBackendNode = false; override isBackendNode = false;
override serialize_widgets = true; override serialize_widgets = true;
outputIndex: number = 0;
inputIndex: number = 0;
changedIndex: number = 1;
displayWidget: ITextWidget; displayWidget: ITextWidget;
override size: Vector2 = [60, 40]; override size: Vector2 = [60, 40];
@@ -55,24 +62,47 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this)) this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this))
} }
formatValue(value: any): string {
return Watch.toString(value)
}
private onValueUpdated(value: any) { private onValueUpdated(value: any) {
console.debug("[Widget] valueUpdated", this, value) console.debug("[Widget] valueUpdated", this, value)
this.displayWidget.value = Watch.toString(value) this.displayWidget.value = this.formatValue(value)
if (this.outputs.length >= this.outputIndex) {
this.setOutputData(this.outputIndex, get(this.value))
}
if (this.outputs.length >= this.changedIndex) {
const changedOutput = this.outputs[this.changedIndex]
if (changedOutput.type === BuiltInSlotType.EVENT)
this.triggerSlot(this.changedIndex, "changed")
}
} }
setValue(value: any) { setValue(value: any) {
this.value.set(value) this.value.set(value)
} }
abstract validateValue(value: any): boolean;
/*
* Logic to run if this widget can be treated as output (slider, combo, text)
*/
override onExecute() { override onExecute() {
// Assumption: we will have one output in the inherited class with the if (this.inputs.length >= this.inputIndex) {
// correct type const data = this.getInputData(this.inputIndex)
this.setOutputData(0, get(this.value)) if (data && this.validateValue(data)) { // TODO can "null" be a legitimate value here?
this.setValue(data)
}
}
if (this.outputs.length >= this.outputIndex) {
this.setOutputData(this.outputIndex, get(this.value))
}
}
const outputLinks = this.getOutputLinks(0) /** Called when a backend node sends a ComfyUI output over a link */
console.debug("[Widget] onExecute", this, outputLinks) receiveOutput() {
// TODO send event to linked nodes
} }
onConnectOutput( onConnectOutput(
@@ -93,7 +123,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
this.setValue(this.properties.defaultValue) this.setValue(this.properties.defaultValue)
const widget = layoutState.findLayoutForNode(this.id) const widget = layoutState.findLayoutForNode(this.id)
if (widget) { if (widget && input.name !== "") {
widget.attrs.title = input.name; widget.attrs.title = input.name;
} }
@@ -164,8 +194,12 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
override svelteComponentType = RangeWidget override svelteComponentType = RangeWidget
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
outputs: [ inputs: [
{ name: "value", type: "number" } { name: "value", type: "number" }
],
outputs: [
{ name: "value", type: "number" },
{ name: "changed", type: BuiltInSlotType.EVENT }
] ]
} }
@@ -173,6 +207,12 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
super(name, 0) super(name, 0)
} }
override validateValue(value: any): boolean {
return typeof value === "number"
&& value >= this.properties.min
&& value <= this.properties.max
}
override clampOneConfig(input: IComfyInputSlot) { override clampOneConfig(input: IComfyInputSlot) {
// this.setProperty("min", clamp(this.properties.min, input.config.min, input.config.max)) // this.setProperty("min", clamp(this.properties.min, input.config.min, input.config.max))
// this.setProperty("max", clamp(this.properties.max, input.config.max, input.config.min)) // this.setProperty("max", clamp(this.properties.max, input.config.max, input.config.min))
@@ -199,8 +239,12 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
} }
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
outputs: [ inputs: [
{ name: "value", type: "string" } { name: "value", type: "string" }
],
outputs: [
{ name: "value", type: "string" },
{ name: "changed", type: BuiltInSlotType.EVENT }
] ]
} }
@@ -236,6 +280,12 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
return true; return true;
} }
override validateValue(value: any): boolean {
if (typeof value !== "string")
return false;
return this.properties.values.indexOf(value) !== -1;
}
override clampOneConfig(input: IComfyInputSlot) { override clampOneConfig(input: IComfyInputSlot) {
if (input.config.values.indexOf(this.properties.value) === -1) { if (input.config.values.indexOf(this.properties.value) === -1) {
if (input.config.values.length === 0) if (input.config.values.length === 0)
@@ -264,8 +314,12 @@ export class ComfyTextNode extends ComfyWidgetNode<string> {
} }
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
outputs: [ inputs: [
{ name: "value", type: "string" } { name: "value", type: "string" }
],
outputs: [
{ name: "value", type: "string" },
{ name: "changed", type: BuiltInSlotType.EVENT }
] ]
} }
@@ -274,6 +328,10 @@ export class ComfyTextNode extends ComfyWidgetNode<string> {
constructor(name?: string) { constructor(name?: string) {
super(name, "") super(name, "")
} }
override validateValue(value: any): boolean {
return typeof value === "string"
}
} }
LiteGraph.registerNodeType({ LiteGraph.registerNodeType({
@@ -282,3 +340,121 @@ LiteGraph.registerNodeType({
desc: "Textbox outputting a string value", desc: "Textbox outputting a string value",
type: "ui/text" type: "ui/text"
}) })
/** Raw output as received from ComfyUI's backend */
export type GalleryOutput = {
images: GalleryOutputEntry[]
}
/** Raw output entry as received from ComfyUI's backend */
export type GalleryOutputEntry = {
filename: string,
subfolder: string,
type: string
}
export interface ComfyGalleryProperties extends ComfyWidgetProperties {
}
export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
override properties: ComfyGalleryProperties = {
defaultValue: []
}
static slotLayout: SlotLayout = {
inputs: [
{ name: "images", type: "OUTPUT" }
]
}
override svelteComponentType = GalleryWidget
constructor(name?: string) {
super(name, [])
}
override afterQueued() {
let queue = get(queueState)
if (!(typeof queue.queueRemaining === "number" && queue.queueRemaining > 1)) {
this.setValue([])
}
}
override formatValue(value: GradioFileData[]): string {
return `Images: ${value.length}`
}
override validateValue(value: any): boolean {
return Array.isArray(value) && value.every(e => "images" in e)
}
receiveOutput() {
const link = this.getInputLink(0)
if (link.data && "images" in link.data) {
const data = link.data as GalleryOutput
console.debug("[ComfyGalleryNode] Received output!", data)
const galleryItems: GradioFileData[] = data.images.map(r => {
// TODO configure backend URL
const url = "http://localhost:8188/view?"
const params = new URLSearchParams(r)
return {
name: null,
data: url + params
}
});
const currentValue = get(this.value)
this.setValue(currentValue.concat(galleryItems))
}
}
}
LiteGraph.registerNodeType({
class: ComfyGalleryNode,
title: "UI.Gallery",
desc: "Gallery that shows most recent outputs",
type: "ui/gallery"
})
export interface ComfyButtonProperties extends ComfyWidgetProperties {
message: string
}
export class ComfyButtonNode extends ComfyWidgetNode<boolean> {
override properties: ComfyButtonProperties = {
defaultValue: false,
message: "bang"
}
static slotLayout: SlotLayout = {
outputs: [
{ name: "event", type: BuiltInSlotType.EVENT },
{ name: "isClicked", type: "boolean" },
]
}
override outputIndex = 1;
override svelteComponentType = ButtonWidget;
override validateValue(value: any): boolean {
return typeof value === "boolean"
}
onClick() {
this.setValue(true)
this.triggerSlot(0, this.properties.message);
this.setValue(false)
}
constructor(name?: string) {
super(name, false)
}
}
LiteGraph.registerNodeType({
class: ComfyButtonNode,
title: "UI.Button",
desc: "Button that triggers an event when clicked",
type: "ui/button"
})

View File

@@ -1,3 +1,3 @@
export { default as ComfyReroute } from "./ComfyReroute" export { default as ComfyReroute } from "./ComfyReroute"
export { ComfySaveImageNode, ComfyPreviewImageNode } from "./ComfyImageNodes"
export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes" export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes"
export { ComfyCopyAction } from "./ComfyActionNodes"

View File

@@ -64,7 +64,7 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
export { ALL_ATTRIBUTES }; export { ALL_ATTRIBUTES };
export type Attributes = { export type Attributes = {
direction: string, direction: "horizontal" | "vertical",
title: string, title: string,
showTitle: boolean, showTitle: boolean,
classes: string classes: string
@@ -95,7 +95,7 @@ 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,
groupItems: (dragItems: IDragItem[], title: string) => ContainerLayout, groupItems: (dragItems: IDragItem[], attrs?: Partial<Attributes>) => ContainerLayout,
ungroup: (container: ContainerLayout) => void, ungroup: (container: ContainerLayout) => void,
getCurrentSelection: () => IDragItem[], getCurrentSelection: () => IDragItem[],
findLayoutForNode: (nodeId: number) => IDragItem | null; findLayoutForNode: (nodeId: number) => IDragItem | null;
@@ -265,7 +265,7 @@ function moveItem(target: IDragItem, to: ContainerLayout, index: number = -1) {
if (entry.parent) { if (entry.parent) {
const parentEntry = state.allItems[entry.parent.id]; const parentEntry = state.allItems[entry.parent.id];
const index = parentEntry.children.indexOf(target) const index = parentEntry.children.findIndex(c => c.id === target.id)
if (index !== -1) { if (index !== -1) {
parentEntry.children.splice(index, 1) parentEntry.children.splice(index, 1)
} }
@@ -293,7 +293,7 @@ function getCurrentSelection(): IDragItem[] {
return state.currentSelection.map(id => state.allItems[id].dragItem) return state.currentSelection.map(id => state.allItems[id].dragItem)
} }
function groupItems(dragItems: IDragItem[], title: string = "Group"): ContainerLayout { function groupItems(dragItems: IDragItem[], attrs: Partial<Attributes> = {}): ContainerLayout {
if (dragItems.length === 0) if (dragItems.length === 0)
return; return;
@@ -305,17 +305,19 @@ function groupItems(dragItems: IDragItem[], title: string = "Group"): ContainerL
let index = undefined; let index = undefined;
if (parent) { if (parent) {
const indexFound = state.allItems[parent.id].children.indexOf(dragItems[0]) const indexFound = state.allItems[parent.id].children.findIndex(c => c.id === dragItems[0].id)
if (indexFound !== -1) if (indexFound !== -1)
index = indexFound index = indexFound
} }
const container = addContainer(parent as ContainerLayout, { title }, index) const container = addContainer(parent as ContainerLayout, attrs, index)
for (const item of dragItems) { for (const item of dragItems) {
moveItem(item, container) moveItem(item, container)
} }
console.debug("[layoutState] Grouped", container, parent, state.allItems[container.id].children, index)
store.set(state) store.set(state)
return container return container
} }
@@ -331,7 +333,7 @@ function ungroup(container: ContainerLayout) {
let index = undefined; let index = undefined;
const parentChildren = state.allItems[parent.id].children; const parentChildren = state.allItems[parent.id].children;
const indexFound = parentChildren.indexOf(container) const indexFound = parentChildren.findIndex(c => c.id === container.id)
if (indexFound !== -1) if (indexFound !== -1)
index = indexFound index = indexFound

View File

@@ -1,12 +1,11 @@
import type { IWidget, LGraphNode } from "@litegraph-js/core"; import type { IWidget, LGraphNode } from "@litegraph-js/core";
import type ComfyApp from "$lib/components/ComfyApp";
import ComfyValueControlWidget from "./widgets/ComfyValueControlWidget"; import ComfyValueControlWidget from "./widgets/ComfyValueControlWidget";
import type { ComfyInputConfig } from "./IComfyInputSlot"; import type { ComfyInputConfig } from "./IComfyInputSlot";
import type IComfyInputSlot from "./IComfyInputSlot"; import type IComfyInputSlot from "./IComfyInputSlot";
import { BuiltInSlotShape } from "@litegraph-ts/core"; import { BuiltInSlotShape } from "@litegraph-ts/core";
import { ComfyComboNode, ComfySliderNode, ComfyTextNode } from "./nodes"; import { ComfyComboNode, ComfySliderNode, ComfyTextNode } from "./nodes";
type WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp) => IComfyInputSlot; type WidgetFactory = (node: LGraphNode, inputName: string, inputData: any) => IComfyInputSlot;
function getNumberDefaults(inputData: any, defaultStep: number): ComfyInputConfig { function getNumberDefaults(inputData: any, defaultStep: number): ComfyInputConfig {
let defaultValue = inputData[1]["default"]; let defaultValue = inputData[1]["default"];
@@ -38,7 +37,7 @@ const INT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any)
return addComfyInput(node, inputName, { type: "number", config, defaultWidgetNode: ComfySliderNode }) return addComfyInput(node, inputName, { type: "number", config, defaultWidgetNode: ComfySliderNode })
}; };
const STRING: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp): IComfyInputSlot => { const STRING: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
const defaultValue = inputData[1].default || ""; const defaultValue = inputData[1].default || "";
const multiline = !!inputData[1].multiline; const multiline = !!inputData[1].multiline;
@@ -54,13 +53,13 @@ const COMBO: WidgetFactory = (node: LGraphNode, inputName: string, inputData: an
return addComfyInput(node, inputName, { type: "string", config: { values: type, defaultValue }, defaultWidgetNode: ComfyComboNode }) return addComfyInput(node, inputName, { type: "string", config: { values: type, defaultValue }, defaultWidgetNode: ComfyComboNode })
} }
const IMAGEUPLOAD: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app): IComfyInputSlot => { const IMAGEUPLOAD: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
return addComfyInput(node, inputName, { type: "number", config: {} }) return addComfyInput(node, inputName, { type: "number", config: {} })
} }
export type WidgetRepository = Record<string, WidgetFactory> export type WidgetRepository = Record<string, WidgetFactory>
export const ComfyWidgets: WidgetRepository = { const ComfyWidgets: WidgetRepository = {
"INT:seed": INT, "INT:seed": INT,
"INT:noise_seed": INT, "INT:noise_seed": INT,
FLOAT, FLOAT,
@@ -69,3 +68,5 @@ export const ComfyWidgets: WidgetRepository = {
COMBO, COMBO,
IMAGEUPLOAD, IMAGEUPLOAD,
} }
export default ComfyWidgets

View File

@@ -0,0 +1,44 @@
<script lang="ts">
import type { ComfyButtonNode } from "$lib/nodes/ComfyWidgetNodes";
import type { ComfySliderNode } from "$lib/nodes/index";
import { type WidgetLayout } from "$lib/stores/layoutState";
import { Button } from "@gradio/button";
import { get, type Writable } from "svelte/store";
export let widget: WidgetLayout | null = null;
let node: ComfyButtonNode | null = null;
let nodeValue: Writable<boolean> | null = null;
let propsChanged: Writable<boolean> | null = null;
$: widget && setNodeValue(widget);
function setNodeValue(widget: WidgetLayout) {
if (widget) {
node = widget.node as ComfyButtonNode
nodeValue = node.value;
propsChanged = node.propsChanged;
}
};
function onClick(e: MouseEvent) {
node.onClick();
}
const style = {
full_width: "100%"
}
</script>
<div class="wrapper gr-button">
{#if node !== null}
<Button on:click={onClick} variant="primary" {style}>
{widget.attrs.title}
</Button>
{/if}
</div>
<style>
.wrapper {
padding: 2px;
width: 100%;
}
</style>

View File

@@ -1,56 +0,0 @@
<script lang="ts">
import { onMount } from "svelte";
import { ImageViewer } from "$lib/ImageViewer";
import { Block } from "@gradio/atoms";
import { Gallery } from "@gradio/gallery";
import type { Styles } from "@gradio/utils";
export let item: WidgetUIState | null = null;
let itemValue: WidgetUIStateStore | null = null; // stores must be declared at top level
$: if(item) {
itemValue = item.value;
}
let style: Styles = {
// grid_cols: [2],
grid: [3],
// object_fit: "cover",
}
let element: HTMLDivElement;
function updateForLightbox() {
// Wait for gradio gallery to show the large preview image, if no timeout then
// the event might fire too early
setTimeout(() => {
const images = element.querySelectorAll<HTMLImageElement>('div.block div > img')
if (images != null) {
images.forEach(ImageViewer.instance.setupImageForLightbox.bind(ImageViewer.instance));
}
ImageViewer.instance.updateOnBackgroundChange();
}, 200)
}
</script>
<div class="wrapper comfy-gallery-widget gr-gallery" bind:this={element}>
{#if item && itemValue}
<Block variant="solid" padding={false}>
<Gallery
bind:value={$itemValue}
label={item.widget.name}
show_label={true}
{style}
root={""}
root_url={""}
on:select={updateForLightbox}
/>
</Block>
{/if}
</div>
<style>
.wrapper {
padding: 2px;
width: 100%;
}
</style>

View File

@@ -1,26 +0,0 @@
import { get } from "svelte/store";
import type { WidgetPanelOptions } from "@litegraph-ts/core";
import ComfyWidget from "./ComfyWidget";
import type { ComfyImageResult } from "$lib/nodes/ComfySaveImageNode";
import queueState from "$lib/stores/queueState";
export type ComfyGalleryEntry = [string, string | null]; // <img> src and alt/title, gradio format
export interface ComfyGalleryWidgetOptions extends WidgetPanelOptions {
}
export default class ComfyGalleryWidget extends ComfyWidget<ComfyGalleryWidgetOptions, ComfyGalleryEntry[]> {
override type = "comfy/gallery";
override isVirtual = true;
addImages(images: ComfyImageResult[]) {
this.setValue(this.value.concat(images));
}
override afterQueued() {
let queue = get(queueState)
if (!(typeof queue.queueRemaining === "number" && queue.queueRemaining > 1)) {
this.setValue([])
}
}
}

View File

@@ -1,54 +0,0 @@
import type { IEnumWidget, IEnumWidgetOptions, INumberWidget, LGraphNode, WidgetPanelOptions } from "@litegraph-ts/core";
import ComfyWidget from "./ComfyWidget";
import type { ComfyImageResult } from "$lib/nodes/ComfySaveImageNode";
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
export interface ComfyValueControlWidgetOptions extends IEnumWidgetOptions {
}
export default class ComfyValueControlWidget extends ComfyWidget<ComfyValueControlWidgetOptions, string> {
override type = "combo";
targetWidget: INumberWidget;
constructor(name: string, value: string, node: ComfyGraphNode, targetWidget: INumberWidget) {
super(name, value, node)
this.targetWidget = targetWidget;
this.options = { values: ["fixed", "increment", "decrement", "randomize"], serialize: false };
}
override afterQueued() {
var v = this.value;
let min = this.targetWidget.options.min;
let max = this.targetWidget.options.max;
// limit to something that javascript can handle
max = Math.min(1125899906842624, max);
min = Math.max(-1125899906842624, min);
let range = (max - min) / (this.targetWidget.options.step);
//adjust values based on valueControl Behaviour
switch (v) {
case "fixed":
break;
case "increment":
this.targetWidget.value += this.targetWidget.options.step;
break;
case "decrement":
this.targetWidget.value -= this.targetWidget.options.step;
break;
case "randomize":
this.targetWidget.value = Math.floor(Math.random() * range) * (this.targetWidget.options.step) + min;
default:
break;
}
/*check if values are over or under their respective
* ranges and set them to min or max.*/
if (this.targetWidget.value < min)
this.targetWidget.value = min;
if (this.targetWidget.value > max)
this.targetWidget.value = max;
// nodeState.widgetStateChanged(this.node.id, this.targetWidget);
}
}

View File

@@ -0,0 +1,78 @@
<script lang="ts">
import { ImageViewer } from "$lib/ImageViewer";
import { Block } from "@gradio/atoms";
import { Gallery } from "@gradio/gallery";
import type { Styles } from "@gradio/utils";
import type { WidgetLayout } from "$lib/stores/layoutState";
import type { Writable } from "svelte/store";
import type { ComfyGalleryNode } from "$lib/nodes/ComfyWidgetNodes";
import type { FileData as GradioFileData } from "@gradio/upload";
export let widget: WidgetLayout | null = null;
let node: ComfyGalleryNode | null = null;
let nodeValue: Writable<GradioFileData[]> | null = null;
let propsChanged: Writable<boolean> | null = null;
let option: number | null = null;
$: widget && setNodeValue(widget);
function setNodeValue(widget: WidgetLayout) {
if (widget) {
node = widget.node as ComfyGalleryNode
nodeValue = node.value;
propsChanged = node.propsChanged;
}
};
let style: Styles = {
// grid_cols: [2],
grid: [3],
// object_fit: "cover",
}
let element: HTMLDivElement;
function updateForLightbox() {
// Wait for gradio gallery to show the large preview image, if no timeout then
// the event might fire too early
setTimeout(() => {
const images = element.querySelectorAll<HTMLImageElement>('div.block div > img')
if (images != null) {
images.forEach(ImageViewer.instance.setupImageForLightbox.bind(ImageViewer.instance));
}
ImageViewer.instance.updateOnBackgroundChange();
}, 200)
}
</script>
<div class="wrapper comfy-gallery-widget gr-gallery" bind:this={element}>
{#if widget && node && nodeValue}
<Block variant="solid" padding={false}>
<div class="padding">
<Gallery
bind:value={$nodeValue}
label={widget.attrs.title}
show_label={true}
{style}
root={""}
root_url={""}
on:select={updateForLightbox}
/>
</div>
</Block>
{/if}
</div>
<style>
.wrapper {
padding: 2px;
width: 100%;
}
.padding {
height: 30rem;
}
.wrapper :global(button.thumbnail-lg) {
width: var(--size-32);
}
</style>