From 546b980c8dbc2c34b679bfa197cffef1e87b0ebc Mon Sep 17 00:00:00 2001
From: space-nuko <24979496+space-nuko@users.noreply.github.com>
Date: Tue, 4 Apr 2023 15:10:09 -0500
Subject: [PATCH] First commit
---
.eslintignore | 13 +
.eslintrc.cjs | 15 +
.gitignore | 10 +
.npmrc | 1 +
.prettierignore | 13 +
.prettierrc | 9 +
README.md | 38 +
jsconfig.json | 17 +
package.json | 36 +
patches/litegraph.js@0.7.12.patch | 14 +
pnpm-lock.yaml | 1653 +++++++++++++++++++++++++
src/app.d.ts | 12 +
src/app.html | 12 +
src/index.test.js | 7 +
src/lib/api.ts | 290 +++++
src/lib/components/ComfyApp.svelte | 24 +
src/lib/components/ComfyApp.ts | 406 ++++++
src/lib/defaultGraph.ts | 119 ++
src/lib/pnginfo.ts | 324 +++++
src/lib/widgets.ts | 137 ++
src/routes/+page.svelte | 5 +
src/routes/+page.ts | 16 +
src/types/litegraph.js/litegraph.d.ts | 1575 +++++++++++++++++++++++
static/favicon.png | Bin 0 -> 1571 bytes
svelte.config.js | 22 +
tsconfig.json | 15 +
vite.config.ts | 16 +
27 files changed, 4799 insertions(+)
create mode 100644 .eslintignore
create mode 100644 .eslintrc.cjs
create mode 100644 .gitignore
create mode 100644 .npmrc
create mode 100644 .prettierignore
create mode 100644 .prettierrc
create mode 100644 README.md
create mode 100644 jsconfig.json
create mode 100644 package.json
create mode 100644 patches/litegraph.js@0.7.12.patch
create mode 100644 pnpm-lock.yaml
create mode 100644 src/app.d.ts
create mode 100644 src/app.html
create mode 100644 src/index.test.js
create mode 100644 src/lib/api.ts
create mode 100644 src/lib/components/ComfyApp.svelte
create mode 100644 src/lib/components/ComfyApp.ts
create mode 100644 src/lib/defaultGraph.ts
create mode 100644 src/lib/pnginfo.ts
create mode 100644 src/lib/widgets.ts
create mode 100644 src/routes/+page.svelte
create mode 100644 src/routes/+page.ts
create mode 100644 src/types/litegraph.js/litegraph.d.ts
create mode 100644 static/favicon.png
create mode 100644 svelte.config.js
create mode 100644 tsconfig.json
create mode 100644 vite.config.ts
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..3897265
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,13 @@
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+
+# Ignore files for PNPM, NPM and YARN
+pnpm-lock.yaml
+package-lock.json
+yarn.lock
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..fab32bf
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,15 @@
+module.exports = {
+ root: true,
+ extends: ['eslint:recommended', 'prettier'],
+ plugins: ['svelte3'],
+ overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
+ parserOptions: {
+ sourceType: 'module',
+ ecmaVersion: 2020
+ },
+ env: {
+ browser: true,
+ es2017: true,
+ node: true
+ }
+};
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6635cf5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..3897265
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,13 @@
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+
+# Ignore files for PNPM, NPM and YARN
+pnpm-lock.yaml
+package-lock.json
+yarn.lock
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..a77fdde
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,9 @@
+{
+ "useTabs": true,
+ "singleQuote": true,
+ "trailingComma": "none",
+ "printWidth": 100,
+ "plugins": ["prettier-plugin-svelte"],
+ "pluginSearchDirs": ["."],
+ "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5c91169
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+# create-svelte
+
+Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
+
+## Creating a project
+
+If you're seeing this, you've probably already done this step. Congrats!
+
+```bash
+# create a new project in the current directory
+npm create svelte@latest
+
+# create a new project in my-app
+npm create svelte@latest my-app
+```
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
+
+```bash
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+To create a production version of your app:
+
+```bash
+npm run build
+```
+
+You can preview the production build with `npm run preview`.
+
+> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..fe45e13
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,17 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true
+ }
+ // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
+ //
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
+ // from the referenced tsconfig.json - TypeScript does not merge them in
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..727e963
--- /dev/null
+++ b/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "web2",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "dev": "vite dev",
+ "build": "vite build",
+ "preview": "vite preview",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
+ "test:unit": "vitest",
+ "lint": "prettier --plugin-search-dir . --check . && eslint .",
+ "format": "prettier --plugin-search-dir . --write .",
+ "svelte-check": "svelte-check"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^2.0.0",
+ "@sveltejs/kit": "^1.5.0",
+ "eslint": "^8.28.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-svelte3": "^4.0.0",
+ "prettier": "^2.8.0",
+ "prettier-plugin-svelte": "^2.8.1",
+ "svelte": "^3.54.0",
+ "svelte-check": "^3.2.0",
+ "typescript": "^5.0.0",
+ "vite": "^4.2.0",
+ "vitest": "^0.25.3"
+ },
+ "type": "module",
+ "dependencies": {
+ "litegraph.js": "^0.7.12",
+ "svelte-preprocess": "^5.0.3",
+ "vite-plugin-full-reload": "^1.0.5"
+ }
+}
diff --git a/patches/litegraph.js@0.7.12.patch b/patches/litegraph.js@0.7.12.patch
new file mode 100644
index 0000000..bcc21da
--- /dev/null
+++ b/patches/litegraph.js@0.7.12.patch
@@ -0,0 +1,14 @@
+diff --git a/src/litegraph.d.ts b/src/litegraph.d.ts
+index c9fbe0ced078548f95382eaa39be2b736be2a1bd..3ee29f82d614cd9f460f56228c56ea4eadc1b3fb 100644
+--- a/src/litegraph.d.ts
++++ b/src/litegraph.d.ts
+@@ -1260,6 +1260,9 @@ export declare class LGraphCanvas {
+ visible_nodes: LGraphNode[];
+ zoom_modify_alpha: boolean;
+
++ release_link_on_empty_shows_menu: boolean;
++ alt_drag_do_clone_nodes: boolean;
++
+ /** clears all the data inside */
+ clear(): void;
+ /** assigns a graph, you can reassign graphs to the same canvas */
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..cdf76ae
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,1653 @@
+lockfileVersion: 5.4
+
+specifiers:
+ '@sveltejs/adapter-auto': ^2.0.0
+ '@sveltejs/kit': ^1.5.0
+ eslint: ^8.28.0
+ eslint-config-prettier: ^8.5.0
+ eslint-plugin-svelte3: ^4.0.0
+ litegraph.js: ^0.7.12
+ prettier: ^2.8.0
+ prettier-plugin-svelte: ^2.8.1
+ svelte: ^3.54.0
+ svelte-check: ^3.2.0
+ svelte-preprocess: ^5.0.3
+ typescript: ^5.0.0
+ vite: ^4.2.0
+ vite-plugin-full-reload: ^1.0.5
+ vitest: ^0.25.3
+
+dependencies:
+ litegraph.js: 0.7.12
+ svelte-preprocess: 5.0.3_ex2livsgfbezl6rd73hucsky7y
+ vite-plugin-full-reload: 1.0.5_vite@4.2.1
+
+devDependencies:
+ '@sveltejs/adapter-auto': 2.0.0_@sveltejs+kit@1.15.0
+ '@sveltejs/kit': 1.15.0_svelte@3.58.0+vite@4.2.1
+ eslint: 8.37.0
+ eslint-config-prettier: 8.8.0_eslint@8.37.0
+ eslint-plugin-svelte3: 4.0.0_4gllgxcu6gmiyy5rrmqexpx7de
+ prettier: 2.8.7
+ prettier-plugin-svelte: 2.10.0_ur5pqdgn24bclu6l6i7qojopk4
+ svelte: 3.58.0
+ svelte-check: 3.2.0_svelte@3.58.0
+ typescript: 5.0.3
+ vite: 4.2.1
+ vitest: 0.25.8
+
+packages:
+
+ /@esbuild/android-arm/0.17.15:
+ resolution: {integrity: sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/android-arm64/0.17.15:
+ resolution: {integrity: sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/android-x64/0.17.15:
+ resolution: {integrity: sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/darwin-arm64/0.17.15:
+ resolution: {integrity: sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/darwin-x64/0.17.15:
+ resolution: {integrity: sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/freebsd-arm64/0.17.15:
+ resolution: {integrity: sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/freebsd-x64/0.17.15:
+ resolution: {integrity: sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-arm/0.17.15:
+ resolution: {integrity: sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-arm64/0.17.15:
+ resolution: {integrity: sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-ia32/0.17.15:
+ resolution: {integrity: sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-loong64/0.17.15:
+ resolution: {integrity: sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-mips64el/0.17.15:
+ resolution: {integrity: sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-ppc64/0.17.15:
+ resolution: {integrity: sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-riscv64/0.17.15:
+ resolution: {integrity: sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-s390x/0.17.15:
+ resolution: {integrity: sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/linux-x64/0.17.15:
+ resolution: {integrity: sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/netbsd-x64/0.17.15:
+ resolution: {integrity: sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/openbsd-x64/0.17.15:
+ resolution: {integrity: sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/sunos-x64/0.17.15:
+ resolution: {integrity: sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/win32-arm64/0.17.15:
+ resolution: {integrity: sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/win32-ia32/0.17.15:
+ resolution: {integrity: sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ optional: true
+
+ /@esbuild/win32-x64/0.17.15:
+ resolution: {integrity: sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ optional: true
+
+ /@eslint-community/eslint-utils/4.4.0_eslint@8.37.0:
+ resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+ dependencies:
+ eslint: 8.37.0
+ eslint-visitor-keys: 3.4.0
+ dev: true
+
+ /@eslint-community/regexpp/4.5.0:
+ resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+ dev: true
+
+ /@eslint/eslintrc/2.0.2:
+ resolution: {integrity: sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.3.4
+ espree: 9.5.1
+ globals: 13.20.0
+ ignore: 5.2.4
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@eslint/js/8.37.0:
+ resolution: {integrity: sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dev: true
+
+ /@humanwhocodes/config-array/0.11.8:
+ resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
+ engines: {node: '>=10.10.0'}
+ dependencies:
+ '@humanwhocodes/object-schema': 1.2.1
+ debug: 4.3.4
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@humanwhocodes/module-importer/1.0.1:
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+ dev: true
+
+ /@humanwhocodes/object-schema/1.2.1:
+ resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
+ dev: true
+
+ /@jridgewell/resolve-uri/3.1.0:
+ resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
+ engines: {node: '>=6.0.0'}
+ dev: true
+
+ /@jridgewell/sourcemap-codec/1.4.14:
+ resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
+
+ /@jridgewell/trace-mapping/0.3.17:
+ resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.0
+ '@jridgewell/sourcemap-codec': 1.4.14
+ dev: true
+
+ /@nodelib/fs.scandir/2.1.5:
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+ dev: true
+
+ /@nodelib/fs.stat/2.0.5:
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+ dev: true
+
+ /@nodelib/fs.walk/1.2.8:
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.15.0
+ dev: true
+
+ /@polka/url/1.0.0-next.21:
+ resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
+ dev: true
+
+ /@sveltejs/adapter-auto/2.0.0_@sveltejs+kit@1.15.0:
+ resolution: {integrity: sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==}
+ peerDependencies:
+ '@sveltejs/kit': ^1.0.0
+ dependencies:
+ '@sveltejs/kit': 1.15.0_svelte@3.58.0+vite@4.2.1
+ import-meta-resolve: 2.2.2
+ dev: true
+
+ /@sveltejs/kit/1.15.0_svelte@3.58.0+vite@4.2.1:
+ resolution: {integrity: sha512-fvDsW9msxWjDU/j9wwLlxEZ6cpXQYcmcQHq7neJMqibMEl39gI1ztVymGnYqM8KLqZXwNmhKtLu8EPheukKtXQ==}
+ engines: {node: ^16.14 || >=18}
+ hasBin: true
+ requiresBuild: true
+ peerDependencies:
+ svelte: ^3.54.0
+ vite: ^4.0.0
+ dependencies:
+ '@sveltejs/vite-plugin-svelte': 2.0.4_svelte@3.58.0+vite@4.2.1
+ '@types/cookie': 0.5.1
+ cookie: 0.5.0
+ devalue: 4.3.0
+ esm-env: 1.0.0
+ kleur: 4.1.5
+ magic-string: 0.30.0
+ mime: 3.0.0
+ sade: 1.8.1
+ set-cookie-parser: 2.6.0
+ sirv: 2.0.2
+ svelte: 3.58.0
+ tiny-glob: 0.2.9
+ undici: 5.21.0
+ vite: 4.2.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@sveltejs/vite-plugin-svelte/2.0.4_svelte@3.58.0+vite@4.2.1:
+ resolution: {integrity: sha512-pjqhW00KwK2uzDGEr+yJBwut+D+4XfJO/+bHHdHzPRXn9+1Jeq5JcFHyrUiYaXgHtyhX0RsllCTm4ssAx4ZY7Q==}
+ 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
+ vite: 4.2.1
+ vitefu: 0.2.4_vite@4.2.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@types/chai-subset/1.3.3:
+ resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
+ dependencies:
+ '@types/chai': 4.3.4
+ dev: true
+
+ /@types/chai/4.3.4:
+ resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==}
+ dev: true
+
+ /@types/cookie/0.5.1:
+ resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
+ dev: true
+
+ /@types/node/18.15.11:
+ resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
+ dev: true
+
+ /@types/pug/2.0.6:
+ resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
+
+ /acorn-jsx/5.3.2_acorn@8.8.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+ dependencies:
+ acorn: 8.8.2
+ dev: true
+
+ /acorn-walk/8.2.0:
+ resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
+ engines: {node: '>=0.4.0'}
+ dev: true
+
+ /acorn/8.8.2:
+ resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+ dev: true
+
+ /ajv/6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+ dev: true
+
+ /ansi-regex/5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /ansi-styles/4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+ dependencies:
+ color-convert: 2.0.1
+ dev: true
+
+ /anymatch/3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+ dev: true
+
+ /argparse/2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+ dev: true
+
+ /assertion-error/1.1.0:
+ resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
+ dev: true
+
+ /balanced-match/1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ /binary-extensions/2.2.0:
+ resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /brace-expansion/1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ /braces/3.0.2:
+ resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+ engines: {node: '>=8'}
+ dependencies:
+ fill-range: 7.0.1
+ dev: true
+
+ /buffer-crc32/0.2.13:
+ resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
+ /busboy/1.6.0:
+ resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+ engines: {node: '>=10.16.0'}
+ dependencies:
+ streamsearch: 1.1.0
+ dev: true
+
+ /callsites/3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /chai/4.3.7:
+ resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==}
+ engines: {node: '>=4'}
+ dependencies:
+ assertion-error: 1.1.0
+ check-error: 1.0.2
+ deep-eql: 4.1.3
+ get-func-name: 2.0.0
+ loupe: 2.3.6
+ pathval: 1.1.1
+ type-detect: 4.0.8
+ dev: true
+
+ /chalk/4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+ dev: true
+
+ /check-error/1.0.2:
+ resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
+ dev: true
+
+ /chokidar/3.5.3:
+ resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+ engines: {node: '>= 8.10.0'}
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.2
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /color-convert/2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+ dependencies:
+ color-name: 1.1.4
+ dev: true
+
+ /color-name/1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ dev: true
+
+ /concat-map/0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ /cookie/0.5.0:
+ resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+ engines: {node: '>= 0.6'}
+ dev: true
+
+ /cross-spawn/7.0.3:
+ resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+ engines: {node: '>= 8'}
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+ dev: true
+
+ /debug/4.3.4:
+ resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.1.2
+ dev: true
+
+ /deep-eql/4.1.3:
+ resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
+ engines: {node: '>=6'}
+ dependencies:
+ type-detect: 4.0.8
+ dev: true
+
+ /deep-is/0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+ dev: true
+
+ /deepmerge/4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /detect-indent/6.1.0:
+ resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+ engines: {node: '>=8'}
+
+ /devalue/4.3.0:
+ resolution: {integrity: sha512-n94yQo4LI3w7erwf84mhRUkUJfhLoCZiLyoOZ/QFsDbcWNZePrLwbQpvZBUG2TNxwV3VjCKPxkiiQA6pe3TrTA==}
+ dev: true
+
+ /doctrine/3.0.0:
+ resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ esutils: 2.0.3
+ dev: true
+
+ /es6-promise/3.3.1:
+ resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
+
+ /esbuild/0.17.15:
+ resolution: {integrity: sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ '@esbuild/android-arm': 0.17.15
+ '@esbuild/android-arm64': 0.17.15
+ '@esbuild/android-x64': 0.17.15
+ '@esbuild/darwin-arm64': 0.17.15
+ '@esbuild/darwin-x64': 0.17.15
+ '@esbuild/freebsd-arm64': 0.17.15
+ '@esbuild/freebsd-x64': 0.17.15
+ '@esbuild/linux-arm': 0.17.15
+ '@esbuild/linux-arm64': 0.17.15
+ '@esbuild/linux-ia32': 0.17.15
+ '@esbuild/linux-loong64': 0.17.15
+ '@esbuild/linux-mips64el': 0.17.15
+ '@esbuild/linux-ppc64': 0.17.15
+ '@esbuild/linux-riscv64': 0.17.15
+ '@esbuild/linux-s390x': 0.17.15
+ '@esbuild/linux-x64': 0.17.15
+ '@esbuild/netbsd-x64': 0.17.15
+ '@esbuild/openbsd-x64': 0.17.15
+ '@esbuild/sunos-x64': 0.17.15
+ '@esbuild/win32-arm64': 0.17.15
+ '@esbuild/win32-ia32': 0.17.15
+ '@esbuild/win32-x64': 0.17.15
+
+ /escape-string-regexp/4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /eslint-config-prettier/8.8.0_eslint@8.37.0:
+ resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==}
+ hasBin: true
+ peerDependencies:
+ eslint: '>=7.0.0'
+ dependencies:
+ eslint: 8.37.0
+ dev: true
+
+ /eslint-plugin-svelte3/4.0.0_4gllgxcu6gmiyy5rrmqexpx7de:
+ resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==}
+ peerDependencies:
+ eslint: '>=8.0.0'
+ svelte: ^3.2.0
+ dependencies:
+ eslint: 8.37.0
+ svelte: 3.58.0
+ dev: true
+
+ /eslint-scope/7.1.1:
+ resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+ dev: true
+
+ /eslint-visitor-keys/3.4.0:
+ resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dev: true
+
+ /eslint/8.37.0:
+ resolution: {integrity: sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ hasBin: true
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0_eslint@8.37.0
+ '@eslint-community/regexpp': 4.5.0
+ '@eslint/eslintrc': 2.0.2
+ '@eslint/js': 8.37.0
+ '@humanwhocodes/config-array': 0.11.8
+ '@humanwhocodes/module-importer': 1.0.1
+ '@nodelib/fs.walk': 1.2.8
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.3
+ debug: 4.3.4
+ doctrine: 3.0.0
+ escape-string-regexp: 4.0.0
+ eslint-scope: 7.1.1
+ eslint-visitor-keys: 3.4.0
+ espree: 9.5.1
+ esquery: 1.5.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 6.0.1
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ globals: 13.20.0
+ grapheme-splitter: 1.0.4
+ ignore: 5.2.4
+ import-fresh: 3.3.0
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ is-path-inside: 3.0.3
+ js-sdsl: 4.4.0
+ js-yaml: 4.1.0
+ json-stable-stringify-without-jsonify: 1.0.1
+ levn: 0.4.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.1
+ strip-ansi: 6.0.1
+ strip-json-comments: 3.1.1
+ text-table: 0.2.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /esm-env/1.0.0:
+ resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==}
+ dev: true
+
+ /espree/9.5.1:
+ resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dependencies:
+ acorn: 8.8.2
+ acorn-jsx: 5.3.2_acorn@8.8.2
+ eslint-visitor-keys: 3.4.0
+ dev: true
+
+ /esquery/1.5.0:
+ resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
+ engines: {node: '>=0.10'}
+ dependencies:
+ estraverse: 5.3.0
+ dev: true
+
+ /esrecurse/4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+ dependencies:
+ estraverse: 5.3.0
+ dev: true
+
+ /estraverse/5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+ dev: true
+
+ /esutils/2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /fast-deep-equal/3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ dev: true
+
+ /fast-glob/3.2.12:
+ resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
+ engines: {node: '>=8.6.0'}
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.5
+ dev: true
+
+ /fast-json-stable-stringify/2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+ dev: true
+
+ /fast-levenshtein/2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ dev: true
+
+ /fastq/1.15.0:
+ resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
+ dependencies:
+ reusify: 1.0.4
+ dev: true
+
+ /file-entry-cache/6.0.1:
+ resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+ dependencies:
+ flat-cache: 3.0.4
+ dev: true
+
+ /fill-range/7.0.1:
+ resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ to-regex-range: 5.0.1
+ dev: true
+
+ /find-up/5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+ dev: true
+
+ /flat-cache/3.0.4:
+ resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+ dependencies:
+ flatted: 3.2.7
+ rimraf: 3.0.2
+ dev: true
+
+ /flatted/3.2.7:
+ resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+ dev: true
+
+ /fs.realpath/1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ /fsevents/2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ optional: true
+
+ /function-bind/1.1.1:
+ resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+
+ /get-func-name/2.0.0:
+ resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
+ dev: true
+
+ /glob-parent/5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+ dependencies:
+ is-glob: 4.0.3
+ dev: true
+
+ /glob-parent/6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+ dependencies:
+ is-glob: 4.0.3
+ dev: true
+
+ /glob/7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ /globals/13.20.0:
+ resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ type-fest: 0.20.2
+ dev: true
+
+ /globalyzer/0.1.0:
+ resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==}
+ dev: true
+
+ /globrex/0.1.2:
+ resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+ dev: true
+
+ /graceful-fs/4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ /grapheme-splitter/1.0.4:
+ resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
+ dev: true
+
+ /has-flag/4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /has/1.0.3:
+ resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+ engines: {node: '>= 0.4.0'}
+ dependencies:
+ function-bind: 1.1.1
+
+ /ignore/5.2.4:
+ resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
+ engines: {node: '>= 4'}
+ dev: true
+
+ /import-fresh/3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+ dev: true
+
+ /import-meta-resolve/2.2.2:
+ resolution: {integrity: sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==}
+ dev: true
+
+ /imurmurhash/0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+ dev: true
+
+ /inflight/1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ /inherits/2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ /is-binary-path/2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+ dependencies:
+ binary-extensions: 2.2.0
+ dev: true
+
+ /is-core-module/2.11.0:
+ resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
+ dependencies:
+ has: 1.0.3
+
+ /is-extglob/2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-glob/4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-extglob: 2.1.1
+ dev: true
+
+ /is-number/7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+ dev: true
+
+ /is-path-inside/3.0.3:
+ resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /isexe/2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+ dev: true
+
+ /js-sdsl/4.4.0:
+ resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==}
+ dev: true
+
+ /js-yaml/4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+ dependencies:
+ argparse: 2.0.1
+ dev: true
+
+ /json-schema-traverse/0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+ dev: true
+
+ /json-stable-stringify-without-jsonify/1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+ dev: true
+
+ /kleur/4.1.5:
+ resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /levn/0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ dev: true
+
+ /litegraph.js/0.7.12:
+ resolution: {integrity: sha512-aQUj5jxKtQLzY0+qQ6YmIfa6EOqhI8lXlJPwEpJFFUir893ulVcDt9YIMFA9rwBDg4/HlFyAUGmwktAduebm9Q==}
+ dev: false
+
+ /local-pkg/0.4.3:
+ resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
+ engines: {node: '>=14'}
+ dev: true
+
+ /locate-path/6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+ dependencies:
+ p-locate: 5.0.0
+ dev: true
+
+ /lodash.merge/4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+ dev: true
+
+ /loupe/2.3.6:
+ resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==}
+ dependencies:
+ get-func-name: 2.0.0
+ dev: true
+
+ /magic-string/0.27.0:
+ resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.14
+
+ /magic-string/0.30.0:
+ resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.14
+ dev: true
+
+ /merge2/1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+ dev: true
+
+ /micromatch/4.0.5:
+ resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+ engines: {node: '>=8.6'}
+ dependencies:
+ braces: 3.0.2
+ picomatch: 2.3.1
+ dev: true
+
+ /mime/3.0.0:
+ resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+ dev: true
+
+ /min-indent/1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ /minimatch/3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ dependencies:
+ brace-expansion: 1.1.11
+
+ /minimist/1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ /mkdirp/0.5.6:
+ resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+ hasBin: true
+ dependencies:
+ minimist: 1.2.8
+
+ /mri/1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /mrmime/1.0.1:
+ resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /ms/2.1.2:
+ resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+ dev: true
+
+ /nanoid/3.3.6:
+ resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ /natural-compare/1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+ dev: true
+
+ /normalize-path/3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /once/1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ dependencies:
+ wrappy: 1.0.2
+
+ /optionator/0.9.1:
+ resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.3
+ dev: true
+
+ /p-limit/3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ yocto-queue: 0.1.0
+ dev: true
+
+ /p-locate/5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+ dependencies:
+ p-limit: 3.1.0
+ dev: true
+
+ /parent-module/1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+ dependencies:
+ callsites: 3.1.0
+ dev: true
+
+ /path-exists/4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /path-is-absolute/1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ /path-key/3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /path-parse/1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ /pathval/1.1.1:
+ resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
+ dev: true
+
+ /picocolors/1.0.0:
+ resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+
+ /picomatch/2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ /postcss/8.4.21:
+ resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.6
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+
+ /prelude-ls/1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+ dev: true
+
+ /prettier-plugin-svelte/2.10.0_ur5pqdgn24bclu6l6i7qojopk4:
+ resolution: {integrity: sha512-GXMY6t86thctyCvQq+jqElO+MKdB09BkL3hexyGP3Oi8XLKRFaJP1ud/xlWCZ9ZIa2BxHka32zhHfcuU+XsRQg==}
+ peerDependencies:
+ prettier: ^1.16.4 || ^2.0.0
+ svelte: ^3.2.0
+ dependencies:
+ prettier: 2.8.7
+ svelte: 3.58.0
+ dev: true
+
+ /prettier/2.8.7:
+ resolution: {integrity: sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+ dev: true
+
+ /punycode/2.3.0:
+ resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /queue-microtask/1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ dev: true
+
+ /readdirp/3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+ dependencies:
+ picomatch: 2.3.1
+ dev: true
+
+ /resolve-from/4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /resolve/1.22.1:
+ resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
+ hasBin: true
+ dependencies:
+ is-core-module: 2.11.0
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ /reusify/1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+ dev: true
+
+ /rimraf/2.7.1:
+ resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.3
+
+ /rimraf/3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.3
+ dev: true
+
+ /rollup/3.20.2:
+ resolution: {integrity: sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==}
+ engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+ hasBin: true
+ optionalDependencies:
+ fsevents: 2.3.2
+
+ /run-parallel/1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ dependencies:
+ queue-microtask: 1.2.3
+ dev: true
+
+ /sade/1.8.1:
+ resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
+ engines: {node: '>=6'}
+ dependencies:
+ mri: 1.2.0
+ dev: true
+
+ /sander/0.5.1:
+ resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==}
+ dependencies:
+ es6-promise: 3.3.1
+ graceful-fs: 4.2.11
+ mkdirp: 0.5.6
+ rimraf: 2.7.1
+
+ /set-cookie-parser/2.6.0:
+ resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
+ dev: true
+
+ /shebang-command/2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+ dependencies:
+ shebang-regex: 3.0.0
+ dev: true
+
+ /shebang-regex/3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /sirv/2.0.2:
+ resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==}
+ engines: {node: '>= 10'}
+ dependencies:
+ '@polka/url': 1.0.0-next.21
+ mrmime: 1.0.1
+ totalist: 3.0.1
+ dev: true
+
+ /sorcery/0.11.0:
+ resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==}
+ hasBin: true
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.14
+ buffer-crc32: 0.2.13
+ minimist: 1.2.8
+ sander: 0.5.1
+
+ /source-map-js/1.0.2:
+ resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ engines: {node: '>=0.10.0'}
+
+ /source-map/0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /streamsearch/1.1.0:
+ resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+ engines: {node: '>=10.0.0'}
+ dev: true
+
+ /strip-ansi/6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+ dependencies:
+ ansi-regex: 5.0.1
+ dev: true
+
+ /strip-indent/3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ min-indent: 1.0.1
+
+ /strip-json-comments/3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /strip-literal/1.0.1:
+ resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==}
+ dependencies:
+ acorn: 8.8.2
+ dev: true
+
+ /supports-color/7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+ dependencies:
+ has-flag: 4.0.0
+ dev: true
+
+ /supports-preserve-symlinks-flag/1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ /svelte-check/3.2.0_svelte@3.58.0:
+ resolution: {integrity: sha512-6ZnscN8dHEN5Eq5LgIzjj07W9nc9myyBH+diXsUAuiY/3rt0l65/LCIQYlIuoFEjp2F1NhXqZiJwV9omPj9tMw==}
+ hasBin: true
+ peerDependencies:
+ svelte: ^3.55.0
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.17
+ chokidar: 3.5.3
+ fast-glob: 3.2.12
+ import-fresh: 3.3.0
+ picocolors: 1.0.0
+ sade: 1.8.1
+ svelte: 3.58.0
+ svelte-preprocess: 5.0.3_ex2livsgfbezl6rd73hucsky7y
+ typescript: 5.0.3
+ transitivePeerDependencies:
+ - '@babel/core'
+ - coffeescript
+ - less
+ - postcss
+ - postcss-load-config
+ - pug
+ - sass
+ - stylus
+ - sugarss
+ dev: true
+
+ /svelte-hmr/0.15.1_svelte@3.58.0:
+ resolution: {integrity: sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==}
+ engines: {node: ^12.20 || ^14.13.1 || >= 16}
+ peerDependencies:
+ svelte: '>=3.19.0'
+ dependencies:
+ svelte: 3.58.0
+ dev: true
+
+ /svelte-preprocess/5.0.3_ex2livsgfbezl6rd73hucsky7y:
+ resolution: {integrity: sha512-GrHF1rusdJVbOZOwgPWtpqmaexkydznKzy5qIC2FabgpFyKN57bjMUUUqPRfbBXK5igiEWn1uO/DXsa2vJ5VHA==}
+ engines: {node: '>= 14.10.0'}
+ requiresBuild: true
+ peerDependencies:
+ '@babel/core': ^7.10.2
+ coffeescript: ^2.5.1
+ less: ^3.11.3 || ^4.0.0
+ postcss: ^7 || ^8
+ postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0
+ pug: ^3.0.0
+ sass: ^1.26.8
+ stylus: ^0.55.0
+ sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0
+ svelte: ^3.23.0
+ typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ coffeescript:
+ optional: true
+ less:
+ optional: true
+ postcss:
+ optional: true
+ postcss-load-config:
+ optional: true
+ pug:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ typescript:
+ optional: true
+ dependencies:
+ '@types/pug': 2.0.6
+ detect-indent: 6.1.0
+ magic-string: 0.27.0
+ sorcery: 0.11.0
+ strip-indent: 3.0.0
+ svelte: 3.58.0
+ typescript: 5.0.3
+
+ /svelte/3.58.0:
+ resolution: {integrity: sha512-brIBNNB76mXFmU/Kerm4wFnkskBbluBDCjx/8TcpYRb298Yh2dztS2kQ6bhtjMcvUhd5ynClfwpz5h2gnzdQ1A==}
+ engines: {node: '>= 8'}
+
+ /text-table/0.2.0:
+ resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+ dev: true
+
+ /tiny-glob/0.2.9:
+ resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
+ dependencies:
+ globalyzer: 0.1.0
+ globrex: 0.1.2
+ dev: true
+
+ /tinybench/2.4.0:
+ resolution: {integrity: sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg==}
+ dev: true
+
+ /tinypool/0.3.1:
+ resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
+ /tinyspy/1.1.1:
+ resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
+ /to-regex-range/5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+ dependencies:
+ is-number: 7.0.0
+ dev: true
+
+ /totalist/3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /type-check/0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ prelude-ls: 1.2.1
+ dev: true
+
+ /type-detect/4.0.8:
+ resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /type-fest/0.20.2:
+ resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /typescript/5.0.3:
+ resolution: {integrity: sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==}
+ engines: {node: '>=12.20'}
+ hasBin: true
+
+ /undici/5.21.0:
+ resolution: {integrity: sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA==}
+ engines: {node: '>=12.18'}
+ dependencies:
+ busboy: 1.6.0
+ dev: true
+
+ /uri-js/4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ dependencies:
+ punycode: 2.3.0
+ dev: true
+
+ /vite-plugin-full-reload/1.0.5_vite@4.2.1:
+ resolution: {integrity: sha512-kVZFDFWr0DxiHn6MuDVTQf7gnWIdETGlZh0hvTiMXzRN80vgF4PKbONSq8U1d0WtHsKaFODTQgJeakLacoPZEQ==}
+ peerDependencies:
+ vite: ^2 || ^3 || ^4
+ dependencies:
+ picocolors: 1.0.0
+ picomatch: 2.3.1
+ vite: 4.2.1
+ dev: false
+
+ /vite/4.2.1:
+ resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==}
+ 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.15
+ postcss: 8.4.21
+ resolve: 1.22.1
+ rollup: 3.20.2
+ optionalDependencies:
+ fsevents: 2.3.2
+
+ /vite/4.2.1_@types+node@18.15.11:
+ resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==}
+ 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:
+ '@types/node': 18.15.11
+ esbuild: 0.17.15
+ postcss: 8.4.21
+ resolve: 1.22.1
+ rollup: 3.20.2
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /vitefu/0.2.4_vite@4.2.1:
+ resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
+ peerDependencies:
+ vite: ^3.0.0 || ^4.0.0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+ dependencies:
+ vite: 4.2.1
+ dev: true
+
+ /vitest/0.25.8:
+ resolution: {integrity: sha512-X75TApG2wZTJn299E/TIYevr4E9/nBo1sUtZzn0Ci5oK8qnpZAZyhwg0qCeMSakGIWtc6oRwcQFyFfW14aOFWg==}
+ engines: {node: '>=v14.16.0'}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@vitest/browser': '*'
+ '@vitest/ui': '*'
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+ dependencies:
+ '@types/chai': 4.3.4
+ '@types/chai-subset': 1.3.3
+ '@types/node': 18.15.11
+ acorn: 8.8.2
+ acorn-walk: 8.2.0
+ chai: 4.3.7
+ debug: 4.3.4
+ local-pkg: 0.4.3
+ source-map: 0.6.1
+ strip-literal: 1.0.1
+ tinybench: 2.4.0
+ tinypool: 0.3.1
+ tinyspy: 1.1.1
+ vite: 4.2.1_@types+node@18.15.11
+ transitivePeerDependencies:
+ - less
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: true
+
+ /which/2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: true
+
+ /word-wrap/1.2.3:
+ resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /wrappy/1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ /yocto-queue/0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+ dev: true
diff --git a/src/app.d.ts b/src/app.d.ts
new file mode 100644
index 0000000..f59b884
--- /dev/null
+++ b/src/app.d.ts
@@ -0,0 +1,12 @@
+// See https://kit.svelte.dev/docs/types#app
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/src/app.html b/src/app.html
new file mode 100644
index 0000000..effe0d0
--- /dev/null
+++ b/src/app.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
+
diff --git a/src/index.test.js b/src/index.test.js
new file mode 100644
index 0000000..e07cbbd
--- /dev/null
+++ b/src/index.test.js
@@ -0,0 +1,7 @@
+import { describe, it, expect } from 'vitest';
+
+describe('sum test', () => {
+ it('adds 1 + 2 to equal 3', () => {
+ expect(1 + 2).toBe(3);
+ });
+});
diff --git a/src/lib/api.ts b/src/lib/api.ts
new file mode 100644
index 0000000..31bc5bb
--- /dev/null
+++ b/src/lib/api.ts
@@ -0,0 +1,290 @@
+type PromptRequestBody = {
+ client_id: string,
+ prompt: any,
+ extra_data: any,
+ front: boolean,
+ number: number | null
+}
+
+export type QueueItemType = "queue" | "history";
+
+export default class ComfyAPI extends EventTarget {
+ private registered: Set = new Set();
+
+ socket: WebSocket | null = null;
+ clientId: string | null = null;
+ hostname: string | null = null;
+ port: number | null = 8188;
+
+ constructor() {
+ super();
+ }
+
+ override addEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: AddEventListenerOptions | boolean) {
+ super.addEventListener(type, callback, options);
+ this.registered.add(type);
+ }
+
+ /**
+ * Poll status for colab and other things that don't support websockets.
+ */
+ private pollQueue() {
+ setInterval(async () => {
+ try {
+ const resp = await fetch(this.getBackendUrl() + "/prompt");
+ const status = await resp.json();
+ this.dispatchEvent(new CustomEvent("status", { detail: status }));
+ } catch (error) {
+ this.dispatchEvent(new CustomEvent("status", { detail: null }));
+ }
+ }, 1000);
+ }
+
+ private getBackendUrl(): string {
+ const hostname = this.hostname || location.hostname;
+ const port = this.port || location.port;
+ console.log(hostname)
+ console.log(port)
+ return `${window.location.protocol}//${hostname}:${port}`
+ }
+
+ /**
+ * Creates and connects a WebSocket for realtime updates
+ * @param {boolean} isReconnect If the socket is connection is a reconnect attempt
+ */
+ private createSocket(isReconnect: boolean = false) {
+ if (this.socket) {
+ return;
+ }
+
+ let opened = false;
+ let existingSession = sessionStorage["Comfy.SessionId"] || "";
+ if (existingSession) {
+ existingSession = "/" + existingSession;
+ }
+
+ const hostname = this.hostname || location.host;
+ const port = this.port || location.port;
+
+ this.socket = new WebSocket(
+ `ws${window.location.protocol === "https:" ? "s" : ""}://${hostname}:${port}/ws${existingSession}`
+ );
+
+ this.socket.addEventListener("open", () => {
+ opened = true;
+ if (isReconnect) {
+ this.dispatchEvent(new CustomEvent("reconnected"));
+ }
+ });
+
+ this.socket.addEventListener("error", () => {
+ if (this.socket) this.socket.close();
+ if (!isReconnect && !opened) {
+ this.pollQueue();
+ }
+ });
+
+ this.socket.addEventListener("close", () => {
+ setTimeout(() => {
+ this.socket = null;
+ this.createSocket(true);
+ }, 300);
+ if (opened) {
+ this.dispatchEvent(new CustomEvent("status", { detail: null }));
+ this.dispatchEvent(new CustomEvent("reconnecting"));
+ }
+ });
+
+ this.socket.addEventListener("message", (event) => {
+ try {
+ const msg = JSON.parse(event.data);
+ switch (msg.type) {
+ case "status":
+ if (msg.data.sid) {
+ this.clientId = msg.data.sid;
+ sessionStorage["Comfy.SessionId"] = this.clientId;
+ }
+ this.dispatchEvent(new CustomEvent("status", { detail: msg.data.status }));
+ break;
+ case "progress":
+ this.dispatchEvent(new CustomEvent("progress", { detail: msg.data }));
+ break;
+ case "executing":
+ this.dispatchEvent(new CustomEvent("executing", { detail: msg.data.node }));
+ break;
+ case "executed":
+ this.dispatchEvent(new CustomEvent("executed", { detail: msg.data }));
+ break;
+ default:
+ if (this.registered.has(msg.type)) {
+ this.dispatchEvent(new CustomEvent(msg.type, { detail: msg.data }));
+ } else {
+ throw new Error("Unknown message type");
+ }
+ }
+ } catch (error) {
+ console.warn("Unhandled message:", event.data);
+ }
+ });
+ }
+
+ /**
+ * Initialises sockets and realtime updates
+ */
+ init() {
+ this.createSocket();
+ }
+
+ /**
+ * Gets a list of extension urls
+ * @returns An array of script urls to import
+ */
+ async getExtensions() {
+ const resp = await fetch(this.getBackendUrl() + `/extensions`, { cache: "no-store" });
+ return await resp.json();
+ }
+
+ /**
+ * Gets a list of embedding names
+ * @returns An array of script urls to import
+ */
+ async getEmbeddings() {
+ const resp = await fetch(this.getBackendUrl() + "/embeddings", { cache: "no-store" });
+ return await resp.json();
+ }
+
+ /**
+ * Loads node object definitions for the graph
+ * @returns The node definitions
+ */
+ async getNodeDefs() {
+ const resp = await fetch(this.getBackendUrl() + "/object_info", { cache: "no-store" });
+ return await resp.json();
+ }
+
+ /**
+ *
+ * @param {number} number The index at which to queue the prompt, passing -1 will insert the prompt at the front of the queue
+ * @param {object} prompt The prompt data to queue
+ */
+ async queuePrompt(number: number, { output, workflow }) {
+ const body: PromptRequestBody = {
+ client_id: this.clientId,
+ prompt: output,
+ extra_data: { extra_pnginfo: { workflow } },
+ front: false,
+ number: null
+ };
+
+ if (number === -1) {
+ body.front = true;
+ } else if (number != 0) {
+ body.number = number;
+ }
+
+ const res = await fetch(this.getBackendUrl() + "/prompt", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ });
+
+ if (res.status !== 200) {
+ throw {
+ response: await res.text(),
+ };
+ }
+ }
+
+ /**
+ * Loads a list of items (queue or history)
+ * @param {string} type The type of items to load, queue or history
+ * @returns The items of the specified type grouped by their status
+ */
+ async getItems(type: QueueItemType) {
+ if (type === "queue") {
+ return this.getQueue();
+ }
+ return this.getHistory();
+ }
+
+ /**
+ * Gets the current state of the queue
+ * @returns The currently running and queued items
+ */
+ async getQueue() {
+ try {
+ const res = await fetch(this.getBackendUrl() + "/queue");
+ const data = await res.json();
+ return {
+ // Running action uses a different endpoint for cancelling
+ Running: data.queue_running.map((prompt) => ({
+ prompt,
+ remove: { name: "Cancel", cb: () => this.interrupt() },
+ })),
+ Pending: data.queue_pending.map((prompt) => ({ prompt })),
+ };
+ } catch (error) {
+ console.error(error);
+ return { Running: [], Pending: [] };
+ }
+ }
+
+ /**
+ * Gets the prompt execution history
+ * @returns Prompt history including node outputs
+ */
+ async getHistory() {
+ try {
+ const res = await fetch(this.getBackendUrl() + "/history");
+ return { History: Object.values(await res.json()) };
+ } catch (error) {
+ console.error(error);
+ return { History: [] };
+ }
+ }
+
+ /**
+ * Sends a POST request to the API
+ * @param {*} type The endpoint to post to
+ * @param {*} body Optional POST data
+ */
+ private async postItem(type: string, body: any) {
+ try {
+ await fetch("/" + type, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: body ? JSON.stringify(body) : undefined,
+ });
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ /**
+ * Deletes an item from the specified list
+ * @param {string} type The type of item to delete, queue or history
+ * @param {number} id The id of the item to delete
+ */
+ async deleteItem(type: string, id: number) {
+ await this.postItem(type, { delete: [id] });
+ }
+
+ /**
+ * Clears the specified list
+ * @param {string} type The type of list to clear, queue or history
+ */
+ async clearItems(type: string) {
+ await this.postItem(type, { clear: true });
+ }
+
+ /**
+ * Interrupts the execution of the running prompt
+ */
+ async interrupt() {
+ await this.postItem("interrupt", null);
+ }
+}
diff --git a/src/lib/components/ComfyApp.svelte b/src/lib/components/ComfyApp.svelte
new file mode 100644
index 0000000..d5c1805
--- /dev/null
+++ b/src/lib/components/ComfyApp.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts
new file mode 100644
index 0000000..c3900e2
--- /dev/null
+++ b/src/lib/components/ComfyApp.ts
@@ -0,0 +1,406 @@
+import { LiteGraph, LGraph, LGraphCanvas, LGraphNode } from "litegraph.js";
+import type { LGraphNodeBase } from "litegraph.js";
+import ComfyAPI from "$lib/api"
+import { ComfyWidgets } from "$lib/widgets"
+import defaultGraph from "$lib/defaultGraph"
+import { getPngMetadata, importA1111 } from "$lib/pnginfo";
+
+type QueueItem = { num: number, batchCount: number }
+
+export default class ComfyApp {
+ api: ComfyAPI;
+ canvasEl: HTMLCanvasElement | null = null;
+ canvasCtx: CanvasRenderingContext2D | null = null;
+ lGraph: LGraph | null = null;
+ lCanvas: LGraphCanvas | null = null;
+ nodeOutputs: Record = {};
+
+ private queueItems: QueueItem[] = [];
+ private processingQueue: boolean = false;
+
+ constructor() {
+ this.api = new ComfyAPI();
+ }
+
+ async setup(): Promise {
+ this.addProcessMouseHandler();
+ this.addProcessKeyHandler();
+
+ this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement;
+ this.lGraph = new LGraph();
+ this.lCanvas = new LGraphCanvas(this.canvasEl, this.lGraph);
+ this.canvasCtx = this.canvasEl.getContext("2d");
+
+ LiteGraph.release_link_on_empty_shows_menu = true;
+ LiteGraph.alt_drag_do_clone_nodes = true;
+
+ this.lGraph.start();
+
+ // Ensure the canvas fills the window
+ this.resizeCanvas();
+ window.addEventListener("resize", this.resizeCanvas.bind(this));
+
+ // await this.#invokeExtensionsAsync("init");
+ await this.registerNodes();
+
+ // Load previous workflow
+ let restored = false;
+ try {
+ const json = localStorage.getItem("workflow");
+ if (json) {
+ const workflow = JSON.parse(json);
+ this.loadGraphData(workflow);
+ restored = true;
+ }
+ } catch (err) {
+ console.error("Error loading previous workflow", err);
+ }
+
+ // We failed to restore a workflow so load the default
+ if (!restored) {
+ this.loadGraphData();
+ }
+
+ // Save current workflow automatically
+ setInterval(() => localStorage.setItem("workflow", JSON.stringify(this.lGraph.serialize())), 1000);
+
+ // this.#addDrawNodeHandler();
+ // this.#addDrawGroupsHandler();
+ // this.#addApiUpdateHandlers();
+ // this.#addDropHandler();
+ // this.#addPasteHandler();
+ // this.#addKeyboardHandler();
+
+ // await this.#invokeExtensionsAsync("setup");
+
+ return Promise.resolve();
+ }
+
+ private resizeCanvas() {
+ this.canvasEl.width = window.innerWidth;
+ this.canvasEl.height = window.innerHeight;
+ this.lCanvas.draw(true, true);
+ }
+
+ private addProcessMouseHandler() {
+
+ }
+
+ private addProcessKeyHandler() {
+
+ }
+
+ private async registerNodes() {
+ const app = this;
+
+ // Load node definitions from the backend
+ const defs = await this.api.getNodeDefs();
+ // await this.#invokeExtensionsAsync("addCustomNodeDefs", defs);
+
+ // Generate list of known widgets
+ const widgets = ComfyWidgets;
+ // const widgets = Object.assign(
+ // {},
+ // ComfyWidgets,
+ // ...(await this.#invokeExtensionsAsync("getCustomWidgets")).filter(Boolean)
+ // );
+
+ // Register a node for each definition
+ for (const nodeId in defs) {
+ const nodeData = defs[nodeId];
+ const node: LGraphNodeBase = Object.assign(
+ function ComfyNode(this: LGraphNode) {
+ 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
+ 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 = true;
+
+ // app.#invokeExtensionsAsync("nodeCreated", this);
+
+ return this;
+ },
+ {
+ title: nodeData.name,
+ comfyClass: nodeData.name,
+ }
+ );
+ node.prototype.comfyClass = nodeData.name;
+
+ // this.#addNodeContextMenuHandler(node);
+ // this.#addDrawBackgroundHandler(node, app);
+
+ // await this.#invokeExtensionsAsync("beforeRegisterNodeDef", node, nodeData);
+ LiteGraph.registerNodeType(nodeId, node);
+ node.category = nodeData.category;
+ }
+
+ // await this.#invokeExtensionsAsync("registerCustomNodes");
+ }
+
+ /**
+ * Populates the graph with the specified workflow data
+ * @param {*} graphData A serialized graph object
+ */
+ loadGraphData(graphData: any = null) {
+ this.clean();
+
+ if (!graphData) {
+ graphData = structuredClone(defaultGraph);
+ }
+
+ // Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
+ for (let n of graphData.nodes) {
+ if (n.type == "T2IAdapterLoader") n.type = "ControlNetLoader";
+ }
+
+ this.lGraph.configure(graphData);
+
+ for (const node of this.lGraph._nodes) {
+ const size = node.computeSize();
+ size[0] = Math.max(node.size[0], size[0]);
+ size[1] = Math.max(node.size[1], size[1]);
+ node.size = size;
+
+ if (node.widgets) {
+ // If you break something in the backend and want to patch workflows in the frontend
+ // This is the place to do this
+ for (let widget of node.widgets) {
+ if (node.type == "KSampler" || node.type == "KSamplerAdvanced") {
+ if (widget.name == "sampler_name") {
+ if (widget.value.constructor === String && widget.value.startsWith("sample_")) {
+ widget.value = widget.value.slice(7);
+ }
+ }
+ }
+ }
+ }
+
+ // this.#invokeExtensions("loadedGraphNode", node);
+ }
+ }
+
+ /**
+ * Converts the current graph workflow for sending to the API
+ * @returns The workflow and node links
+ */
+ async graphToPrompt() {
+ const workflow = this.lGraph.serialize();
+ const output = {};
+ // Process nodes in order of execution
+ for (const node of this.lGraph.computeExecutionOrder(false, null)) {
+ const n = workflow.nodes.find((n) => n.id === node.id);
+
+ if (node.isVirtualNode) {
+ // Don't serialize frontend only nodes but let them make changes
+ if (node.applyToGraph) {
+ node.applyToGraph(workflow);
+ }
+ continue;
+ }
+
+ if (node.mode === 2) {
+ // Don't serialize muted nodes
+ continue;
+ }
+
+ const inputs = {};
+ const widgets = node.widgets;
+
+ // Store all widget values
+ if (widgets) {
+ for (const i in widgets) {
+ const widget = widgets[i];
+ if (!widget.options || widget.options.serialize !== false) {
+ inputs[widget.name] = widget.serializeValue ? await widget.serializeValue(n, i) : widget.value;
+ }
+ }
+ }
+
+ // Store all node links
+ for (let i in node.inputs) {
+ let parent = node.getInputNode(i);
+ if (parent) {
+ let link = node.getInputLink(i);
+ while (parent && parent.isVirtualNode) {
+ link = parent.getInputLink(link.origin_slot);
+ if (link) {
+ parent = parent.getInputNode(link.origin_slot);
+ } else {
+ parent = null;
+ }
+ }
+
+ if (link) {
+ inputs[node.inputs[i].name] = [String(link.origin_id), parseInt(link.origin_slot)];
+ }
+ }
+ }
+
+ output[String(node.id)] = {
+ inputs,
+ class_type: node.comfyClass,
+ };
+ }
+
+ // Remove inputs connected to removed nodes
+
+ for (const o in output) {
+ for (const i in output[o].inputs) {
+ if (Array.isArray(output[o].inputs[i])
+ && output[o].inputs[i].length === 2
+ && !output[output[o].inputs[i][0]]) {
+ delete output[o].inputs[i];
+ }
+ }
+ }
+
+ return { workflow, output };
+ }
+
+ async queuePrompt(num: number, batchCount: number = 1) {
+ this.queueItems.push({ num, batchCount });
+
+ // Only have one action process the items so each one gets a unique seed correctly
+ if (this.processingQueue) {
+ return;
+ }
+
+ this.processingQueue = true;
+ try {
+ while (this.queueItems.length) {
+ ({ num, batchCount } = this.queueItems.pop());
+ console.log(`Queue get! ${num} ${batchCount}`);
+
+ for (let i = 0; i < batchCount; i++) {
+ const p = await this.graphToPrompt();
+
+ try {
+ await this.api.queuePrompt(num, p);
+ } catch (error) {
+ // this.ui.dialog.show(error.response || error.toString());
+ console.error(error.response || error.toString())
+ break;
+ }
+
+ for (const n of p.workflow.nodes) {
+ const node = this.lGraph.getNodeById(n.id);
+ if (node.widgets) {
+ for (const widget of node.widgets) {
+ // Allow widgets to run callbacks after a prompt has been queued
+ // e.g. random seed after every gen
+ // if (widget.afterQueued) {
+ // widget.afterQueued();
+ // }
+ }
+ }
+ }
+
+ this.lCanvas.draw(true, true);
+ // await this.ui.queue.update();
+ }
+ }
+ } finally {
+ console.log("Queue finished!");
+ this.processingQueue = false;
+ }
+ }
+
+ /**
+ * Loads workflow data from the specified file
+ */
+ async handleFile(file: File) {
+ if (file.type === "image/png") {
+ const pngInfo = await getPngMetadata(file);
+ if (pngInfo) {
+ if (pngInfo.workflow) {
+ this.loadGraphData(JSON.parse(pngInfo.workflow));
+ } else if (pngInfo.parameters) {
+ importA1111(this.lGraph, pngInfo.parameters);
+ }
+ }
+ } else if (file.type === "application/json" || file.name.endsWith(".json")) {
+ const reader = new FileReader();
+ reader.onload = () => {
+ this.loadGraphData(JSON.parse(reader.result));
+ };
+ reader.readAsText(file);
+ }
+ }
+
+ // registerExtension(extension) {
+ // if (!extension.name) {
+ // throw new Error("Extensions must have a 'name' property.");
+ // }
+ // if (this.extensions.find((ext) => ext.name === extension.name)) {
+ // throw new Error(`Extension named '${extension.name}' already registered.`);
+ // }
+ // this.extensions.push(extension);
+ // }
+
+ /**
+ * Refresh combo list on whole nodes
+ */
+ async refreshComboInNodes() {
+ const defs = await this.api.getNodeDefs();
+
+ for(let nodeNum in this.lGraph._nodes) {
+ const node = this.lGraph._nodes[nodeNum];
+
+ const def = defs[node.type];
+
+ for(const widgetNum in node.widgets) {
+ const widget = node.widgets[widgetNum]
+
+ if(widget.type == "combo" && def["input"]["required"][widget.name] !== undefined) {
+ widget.options.values = def["input"]["required"][widget.name][0];
+
+ if(!widget.options.values.includes(widget.value)) {
+ widget.value = widget.options.values[0];
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Clean current state
+ */
+ clean() {
+ this.nodeOutputs = {};
+ }
+}
diff --git a/src/lib/defaultGraph.ts b/src/lib/defaultGraph.ts
new file mode 100644
index 0000000..f735d64
--- /dev/null
+++ b/src/lib/defaultGraph.ts
@@ -0,0 +1,119 @@
+export default {
+ last_node_id: 9,
+ last_link_id: 9,
+ nodes: [
+ {
+ id: 7,
+ type: "CLIPTextEncode",
+ pos: [413, 389],
+ size: { 0: 425.27801513671875, 1: 180.6060791015625 },
+ flags: {},
+ order: 3,
+ mode: 0,
+ inputs: [{ name: "clip", type: "CLIP", link: 5 }],
+ outputs: [{ name: "CONDITIONING", type: "CONDITIONING", links: [6], slot_index: 0 }],
+ properties: {},
+ widgets_values: ["bad hands"],
+ },
+ {
+ id: 6,
+ type: "CLIPTextEncode",
+ pos: [415, 186],
+ size: { 0: 422.84503173828125, 1: 164.31304931640625 },
+ flags: {},
+ order: 2,
+ mode: 0,
+ inputs: [{ name: "clip", type: "CLIP", link: 3 }],
+ outputs: [{ name: "CONDITIONING", type: "CONDITIONING", links: [4], slot_index: 0 }],
+ properties: {},
+ widgets_values: ["masterpiece best quality girl"],
+ },
+ {
+ id: 5,
+ type: "EmptyLatentImage",
+ pos: [473, 609],
+ size: { 0: 315, 1: 106 },
+ flags: {},
+ order: 1,
+ mode: 0,
+ outputs: [{ name: "LATENT", type: "LATENT", links: [2], slot_index: 0 }],
+ properties: {},
+ widgets_values: [512, 512, 1],
+ },
+ {
+ id: 3,
+ type: "KSampler",
+ pos: [863, 186],
+ size: { 0: 315, 1: 262 },
+ flags: {},
+ order: 4,
+ mode: 0,
+ inputs: [
+ { name: "model", type: "MODEL", link: 1 },
+ { name: "positive", type: "CONDITIONING", link: 4 },
+ { name: "negative", type: "CONDITIONING", link: 6 },
+ { name: "latent_image", type: "LATENT", link: 2 },
+ ],
+ outputs: [{ name: "LATENT", type: "LATENT", links: [7], slot_index: 0 }],
+ properties: {},
+ widgets_values: [8566257, true, 20, 8, "euler", "normal", 1],
+ },
+ {
+ id: 8,
+ type: "VAEDecode",
+ pos: [1209, 188],
+ size: { 0: 210, 1: 46 },
+ flags: {},
+ order: 5,
+ mode: 0,
+ inputs: [
+ { name: "samples", type: "LATENT", link: 7 },
+ { name: "vae", type: "VAE", link: 8 },
+ ],
+ outputs: [{ name: "IMAGE", type: "IMAGE", links: [9], slot_index: 0 }],
+ properties: {},
+ },
+ {
+ id: 9,
+ type: "SaveImage",
+ pos: [1451, 189],
+ size: { 0: 210, 1: 26 },
+ flags: {},
+ order: 6,
+ mode: 0,
+ inputs: [{ name: "images", type: "IMAGE", link: 9 }],
+ properties: {},
+ },
+ {
+ id: 4,
+ type: "CheckpointLoaderSimple",
+ pos: [26, 474],
+ size: { 0: 315, 1: 98 },
+ flags: {},
+ order: 0,
+ mode: 0,
+ outputs: [
+ { name: "MODEL", type: "MODEL", links: [1], slot_index: 0 },
+ { name: "CLIP", type: "CLIP", links: [3, 5], slot_index: 1 },
+ { name: "VAE", type: "VAE", links: [8], slot_index: 2 },
+ ],
+ properties: {},
+ widgets_values: ["v1-5-pruned-emaonly.ckpt"],
+ },
+ ],
+ links: [
+ [1, 4, 0, 3, 0, "MODEL"],
+ [2, 5, 0, 3, 3, "LATENT"],
+ [3, 4, 1, 6, 0, "CLIP"],
+ [4, 6, 0, 3, 1, "CONDITIONING"],
+ [5, 4, 1, 7, 0, "CLIP"],
+ [6, 7, 0, 3, 2, "CONDITIONING"],
+ [7, 3, 0, 8, 0, "LATENT"],
+ [8, 4, 2, 8, 1, "VAE"],
+ [9, 8, 0, 9, 0, "IMAGE"],
+ ],
+ groups: [],
+ config: {},
+ extra: {},
+ version: 0.4,
+};
diff --git a/src/lib/pnginfo.ts b/src/lib/pnginfo.ts
new file mode 100644
index 0000000..92b21e9
--- /dev/null
+++ b/src/lib/pnginfo.ts
@@ -0,0 +1,324 @@
+import { LiteGraph, LGraph, LGraphNode } from "litegraph.js"
+import type ComfyAPI from "$lib/api"
+
+class PNGMetadataPromise extends Promise> {
+ public cancelMethod: () => void;
+ constructor(executor: (resolve: (value?: Record) => void, reject: (reason?: any) => void) => void) {
+ super(executor);
+
+ }
+
+ //cancel the operation
+ public cancel() {
+ if (this.cancelMethod) {
+ this.cancelMethod();
+ }
+ }
+}
+
+export function getPngMetadata(file: File): PNGMetadataPromise {
+ return new PNGMetadataPromise((r, _) => {
+ const reader = new FileReader();
+ reader.onload = (event: Event) => {
+ // Get the PNG data as a Uint8Array
+ const pngData = new Uint8Array((event.target as any).result);
+ const dataView = new DataView(pngData.buffer);
+
+ // Check that the PNG signature is present
+ if (dataView.getUint32(0) !== 0x89504e47) {
+ console.error("Not a valid PNG file");
+ r();
+ return;
+ }
+
+ // Start searching for chunks after the PNG signature
+ let offset = 8;
+ let txt_chunks = {};
+ // Loop through the chunks in the PNG file
+ while (offset < pngData.length) {
+ // Get the length of the chunk
+ const length = dataView.getUint32(offset);
+ // Get the chunk type
+ const type = String.fromCharCode(...pngData.slice(offset + 4, offset + 8));
+ if (type === "tEXt") {
+ // Get the keyword
+ let keyword_end = offset + 8;
+ while (pngData[keyword_end] !== 0) {
+ keyword_end++;
+ }
+ const keyword = String.fromCharCode(...pngData.slice(offset + 8, keyword_end));
+ // Get the text
+ const text = String.fromCharCode(...pngData.slice(keyword_end + 1, offset + 8 + length));
+ txt_chunks[keyword] = text;
+ }
+
+ offset += 12 + length;
+ }
+
+ r(txt_chunks);
+ };
+
+ reader.readAsArrayBuffer(file);
+ });
+}
+
+type NodeIndex = { node: LGraphNode, index: number }
+
+export async function importA1111(graph: LGraph, parameters: string, api: ComfyAPI) {
+ const p = parameters.lastIndexOf("\nSteps:");
+ if (p > -1) {
+ const embeddings = await api.getEmbeddings();
+ const opts = parameters
+ .substr(p)
+ .split(",")
+ .reduce((p, n) => {
+ const s = n.split(":");
+ p[s[0].trim().toLowerCase()] = s[1].trim();
+ return p;
+ }, {});
+ const p2 = parameters.lastIndexOf("\nNegative prompt:", p);
+ if (p2 > -1) {
+ let positive = parameters.substr(0, p2).trim();
+ let negative = parameters.substring(p2 + 18, p).trim();
+
+ const ckptNode = LiteGraph.createNode("CheckpointLoaderSimple");
+ const clipSkipNode = LiteGraph.createNode("CLIPSetLastLayer");
+ const positiveNode = LiteGraph.createNode("CLIPTextEncode");
+ const negativeNode = LiteGraph.createNode("CLIPTextEncode");
+ const samplerNode = LiteGraph.createNode("KSampler");
+ const imageNode = LiteGraph.createNode("EmptyLatentImage");
+ const vaeNode = LiteGraph.createNode("VAEDecode");
+ const vaeLoaderNode = LiteGraph.createNode("VAELoader");
+ const saveNode = LiteGraph.createNode("SaveImage");
+ let hrSamplerNode = null;
+
+ const ceil64 = (v) => Math.ceil(v / 64) * 64;
+
+ function getWidget(node: LGraphNode, name: string) {
+ return node.widgets.find((w) => w.name === name);
+ }
+
+ function setWidgetValue(node: LGraphNode, name: string, value: any, isOptionPrefix: boolean = false) {
+ const w = getWidget(node, name);
+ if (isOptionPrefix) {
+ const o = w.options.values.find((w) => w.startsWith(value));
+ if (o) {
+ w.value = o;
+ } else {
+ console.warn(`Unknown value '${value}' for widget '${name}'`, node);
+ w.value = value;
+ }
+ } else {
+ w.value = value;
+ }
+ }
+
+ function createLoraNodes(clipNode: LGraphNode, text: string, prevClip: NodeIndex, prevModel: NodeIndex) {
+ const loras = [];
+ text = text.replace(/]+)>/g, function (m, c) {
+ const s = c.split(":");
+ const weight = parseFloat(s[1]);
+ if (isNaN(weight)) {
+ console.warn("Invalid LORA", m);
+ } else {
+ loras.push({ name: s[0], weight });
+ }
+ return "";
+ });
+
+ for (const l of loras) {
+ const loraNode = LiteGraph.createNode("LoraLoader");
+ graph.add(loraNode);
+ setWidgetValue(loraNode, "lora_name", l.name, true);
+ setWidgetValue(loraNode, "strength_model", l.weight);
+ setWidgetValue(loraNode, "strength_clip", l.weight);
+ prevModel.node.connect(prevModel.index, loraNode, 0);
+ prevClip.node.connect(prevClip.index, loraNode, 1);
+ prevModel = { node: loraNode, index: 0 };
+ prevClip = { node: loraNode, index: 1 };
+ }
+
+ prevClip.node.connect(1, clipNode, 0);
+ prevModel.node.connect(0, samplerNode, 0);
+ if (hrSamplerNode) {
+ prevModel.node.connect(0, hrSamplerNode, 0);
+ }
+
+ return { text, prevModel, prevClip };
+ }
+
+ function replaceEmbeddings(text: string) {
+ return text.replaceAll(
+ new RegExp(
+ "\\b(" + embeddings.map((e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\b|\\b") + ")\\b",
+ "ig"
+ ),
+ "embedding:$1"
+ );
+ }
+
+ function popOpt(name: string) {
+ const v = opts[name];
+ delete opts[name];
+ return v;
+ }
+
+ graph.clear();
+ graph.add(ckptNode);
+ graph.add(clipSkipNode);
+ graph.add(positiveNode);
+ graph.add(negativeNode);
+ graph.add(samplerNode);
+ graph.add(imageNode);
+ graph.add(vaeNode);
+ graph.add(vaeLoaderNode);
+ graph.add(saveNode);
+
+ ckptNode.connect(1, clipSkipNode, 0);
+ clipSkipNode.connect(0, positiveNode, 0);
+ clipSkipNode.connect(0, negativeNode, 0);
+ ckptNode.connect(0, samplerNode, 0);
+ positiveNode.connect(0, samplerNode, 1);
+ negativeNode.connect(0, samplerNode, 2);
+ imageNode.connect(0, samplerNode, 3);
+ vaeNode.connect(0, saveNode, 0);
+ samplerNode.connect(0, vaeNode, 0);
+ vaeLoaderNode.connect(0, vaeNode, 1);
+
+ const handlers = {
+ model(v: string) {
+ setWidgetValue(ckptNode, "ckpt_name", v, true);
+ },
+ "cfg scale"(v: number) {
+ setWidgetValue(samplerNode, "cfg", +v);
+ },
+ "clip skip"(v: number) {
+ setWidgetValue(clipSkipNode, "stop_at_clip_layer", -v);
+ },
+ sampler(v: string) {
+ let name = v.toLowerCase().replace("++", "pp").replaceAll(" ", "_");
+ if (name.includes("karras")) {
+ name = name.replace("karras", "").replace(/_+$/, "");
+ setWidgetValue(samplerNode, "scheduler", "karras");
+ } else {
+ setWidgetValue(samplerNode, "scheduler", "normal");
+ }
+ const w = getWidget(samplerNode, "sampler_name");
+ const o = w.options.values.find((w) => w === name || w === "sample_" + name);
+ if (o) {
+ setWidgetValue(samplerNode, "sampler_name", o);
+ }
+ },
+ size(v: string) {
+ const wxh = v.split("x");
+ const w = ceil64(+wxh[0]);
+ const h = ceil64(+wxh[1]);
+ const hrUp = popOpt("hires upscale");
+ const hrSz = popOpt("hires resize");
+ let hrMethod = popOpt("hires upscaler");
+
+ setWidgetValue(imageNode, "width", w);
+ setWidgetValue(imageNode, "height", h);
+
+ if (hrUp || hrSz) {
+ let uw, uh;
+ if (hrUp) {
+ uw = w * hrUp;
+ uh = h * hrUp;
+ } else {
+ const s = hrSz.split("x");
+ uw = +s[0];
+ uh = +s[1];
+ }
+
+ let upscaleNode: LGraphNode;
+ let latentNode: LGraphNode;
+
+ if (hrMethod.startsWith("Latent")) {
+ latentNode = upscaleNode = LiteGraph.createNode("LatentUpscale");
+ graph.add(upscaleNode);
+ samplerNode.connect(0, upscaleNode, 0);
+
+ switch (hrMethod) {
+ case "Latent (nearest-exact)":
+ hrMethod = "nearest-exact";
+ break;
+ }
+ setWidgetValue(upscaleNode, "upscale_method", hrMethod, true);
+ } else {
+ const decode = LiteGraph.createNode("VAEDecodeTiled");
+ graph.add(decode);
+ samplerNode.connect(0, decode, 0);
+ vaeLoaderNode.connect(0, decode, 1);
+
+ const upscaleLoaderNode = LiteGraph.createNode("UpscaleModelLoader");
+ graph.add(upscaleLoaderNode);
+ setWidgetValue(upscaleLoaderNode, "model_name", hrMethod, true);
+
+ const modelUpscaleNode = LiteGraph.createNode("ImageUpscaleWithModel");
+ graph.add(modelUpscaleNode);
+ decode.connect(0, modelUpscaleNode, 1);
+ upscaleLoaderNode.connect(0, modelUpscaleNode, 0);
+
+ upscaleNode = LiteGraph.createNode("ImageScale");
+ graph.add(upscaleNode);
+ modelUpscaleNode.connect(0, upscaleNode, 0);
+
+ const vaeEncodeNode = (latentNode = LiteGraph.createNode("VAEEncodeTiled"));
+ graph.add(vaeEncodeNode);
+ upscaleNode.connect(0, vaeEncodeNode, 0);
+ vaeLoaderNode.connect(0, vaeEncodeNode, 1);
+ }
+
+ setWidgetValue(upscaleNode, "width", ceil64(uw));
+ setWidgetValue(upscaleNode, "height", ceil64(uh));
+
+ hrSamplerNode = LiteGraph.createNode("KSampler");
+ graph.add(hrSamplerNode);
+ ckptNode.connect(0, hrSamplerNode, 0);
+ positiveNode.connect(0, hrSamplerNode, 1);
+ negativeNode.connect(0, hrSamplerNode, 2);
+ latentNode.connect(0, hrSamplerNode, 3);
+ hrSamplerNode.connect(0, vaeNode, 0);
+ }
+ },
+ steps(v: number) {
+ setWidgetValue(samplerNode, "steps", +v);
+ },
+ seed(v: number) {
+ setWidgetValue(samplerNode, "seed", +v);
+ },
+ };
+
+ for (const opt in opts) {
+ if (opt in handlers) {
+ handlers[opt](popOpt(opt));
+ }
+ }
+
+ if (hrSamplerNode) {
+ setWidgetValue(hrSamplerNode, "steps", getWidget(samplerNode, "steps").value);
+ setWidgetValue(hrSamplerNode, "cfg", getWidget(samplerNode, "cfg").value);
+ setWidgetValue(hrSamplerNode, "scheduler", getWidget(samplerNode, "scheduler").value);
+ setWidgetValue(hrSamplerNode, "sampler_name", getWidget(samplerNode, "sampler_name").value);
+ setWidgetValue(hrSamplerNode, "denoise", +(popOpt("denoising strength") || "1"));
+ }
+
+ let n = createLoraNodes(positiveNode, positive, { node: clipSkipNode, index: 0 }, { node: ckptNode, index: 0 });
+ positive = n.text;
+ n = createLoraNodes(negativeNode, negative, n.prevClip, n.prevModel);
+ negative = n.text;
+
+ setWidgetValue(positiveNode, "text", replaceEmbeddings(positive));
+ setWidgetValue(negativeNode, "text", replaceEmbeddings(negative));
+
+ graph.arrange();
+
+ for (const opt of ["model hash", "ensd"]) {
+ delete opts[opt];
+ }
+
+ console.warn("Unhandled parameters:", opts);
+ }
+ }
+}
diff --git a/src/lib/widgets.ts b/src/lib/widgets.ts
new file mode 100644
index 0000000..ca86166
--- /dev/null
+++ b/src/lib/widgets.ts
@@ -0,0 +1,137 @@
+import type { IWidget, LGraphNode } from "litegraph.js";
+import type ComfyApp from "$lib/components/ComfyApp";
+
+interface WidgetData {
+ widget: IWidget,
+ minWidth?: number,
+ minHeight?: number
+}
+
+type WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp) => WidgetData;
+
+
+type NumberConfig = { min: number, max: number, step: number, precision: number }
+type NumberDefaults = { val: number, config: NumberConfig }
+
+function getNumberDefaults(inputData: any, defaultStep: number): NumberDefaults {
+ let defaultVal = inputData[1]["default"];
+ let { min, max, step } = inputData[1];
+
+ if (defaultVal == undefined) defaultVal = 0;
+ if (min == undefined) min = 0;
+ if (max == undefined) max = 2048;
+ if (step == undefined) step = defaultStep;
+
+ return { val: defaultVal, config: { min, max, step: 10.0 * step, precision: 0 } };
+}
+
+
+const FLOAT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): WidgetData => {
+ const { val, config } = getNumberDefaults(inputData, 0.5);
+ return { widget: node.addWidget("number", inputName, val, () => {}, config) };
+}
+
+
+const INT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): WidgetData => {
+ const { val, config } = getNumberDefaults(inputData, 1);
+ return {
+ widget: node.addWidget(
+ "number",
+ inputName,
+ val,
+ function (v) {
+ const s = this.options.step / 10;
+ this.value = Math.round(v / s) * s;
+ },
+ config
+ ),
+ };
+};
+
+const STRING: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp): WidgetData => {
+ const defaultVal = inputData[1].default || "";
+ const multiline = !!inputData[1].multiline;
+
+ // if (multiline) {
+ // return addMultilineWidget(node, inputName, { defaultVal, ...inputData[1] }, app);
+ // } else {
+ return { widget: node.addWidget("text", inputName, defaultVal, () => {}, {}) };
+ // }
+};
+
+const COMBO: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): WidgetData => {
+ const type = inputData[0];
+ let defaultValue = type[0];
+ if (inputData[1] && inputData[1].default) {
+ defaultValue = inputData[1].default;
+ }
+ return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { values: type }) };
+}
+
+const IMAGEUPLOAD: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app): WidgetData => {
+ const imageWidget = node.widgets.find((w) => w.name === "image");
+ let uploadWidget: IWidget;
+
+ // async function uploadFile(file: File, updateNode: boolean) {
+ // try {
+ // // Wrap file in formdata so it includes filename
+ // const body = new FormData();
+ // body.append("image", file);
+ // const resp = await fetch("/upload/image", {
+ // method: "POST",
+ // body,
+ // });
+
+ // if (resp.status === 200) {
+ // const data = await resp.json();
+ // // Add the file as an option and update the widget value
+ // if (!imageWidget.options.values.includes(data.name)) {
+ // imageWidget.options.values.push(data.name);
+ // }
+
+ // if (updateNode) {
+ // // showImage(data.name);
+ // imageWidget.value = data.name;
+ // }
+ // } else {
+ // alert(resp.status + " - " + resp.statusText);
+ // }
+ // } catch (error) {
+ // alert(error);
+ // }
+ // }
+
+ // const fileInput = document.createElement("input");
+ // Object.assign(fileInput, {
+ // type: "file",
+ // accept: "image/jpeg,image/png",
+ // style: "display: none",
+ // onchange: async () => {
+ // if (fileInput.files.length) {
+ // await uploadFile(fileInput.files[0], true);
+ // }
+ // },
+ // });
+ // document.body.append(fileInput);
+
+ // Create the button widget for selecting the files
+ uploadWidget = node.addWidget("button", "choose file to upload", "image", () => {
+ // fileInput.click();
+ });
+ uploadWidget.options = { serialize: false };
+
+ return { widget: uploadWidget };
+}
+
+
+export type WidgetRepository = Record
+
+export const ComfyWidgets: WidgetRepository = {
+ "INT:seed": INT,
+ "INT:noise_seed": INT,
+ FLOAT,
+ INT,
+ STRING,
+ COMBO,
+ IMAGEUPLOAD,
+}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
new file mode 100644
index 0000000..091e37a
--- /dev/null
+++ b/src/routes/+page.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/src/routes/+page.ts b/src/routes/+page.ts
new file mode 100644
index 0000000..719b517
--- /dev/null
+++ b/src/routes/+page.ts
@@ -0,0 +1,16 @@
+import type { PageLoad } from "./$types"
+
+// `PageServerData` will contain everything from the layouts and also the
+// `data` from the `+page.server.ts` file.
+type OutputProps = {}
+
+// We have imported the `PageLoad` type from the relative `./$types` folder that
+// is hidden in the generated `.svelte-kit` folder. Those generated types
+// contain a `PageLoad` type with a `params` and `data` object that matches our route.
+// You need to run the dev server or `svelte-kit sync` to generate them.
+export const load: PageLoad = async ({
+ params,
+ data,
+}) => {
+ return {}
+}
diff --git a/src/types/litegraph.js/litegraph.d.ts b/src/types/litegraph.js/litegraph.d.ts
new file mode 100644
index 0000000..670c28f
--- /dev/null
+++ b/src/types/litegraph.js/litegraph.d.ts
@@ -0,0 +1,1575 @@
+// Type definitions for litegraph.js 0.7.0
+// Project: litegraph.js
+// Definitions by: NateScarlet
+
+declare module "litegraph.js" {
+ export type Vector2 = [number, number];
+ export type Vector4 = [number, number, number, number];
+ export type widgetTypes =
+ | "number"
+ | "slider"
+ | "combo"
+ | "text"
+ | "toggle"
+ | "button";
+ export type SlotShape =
+ | typeof LiteGraph.BOX_SHAPE
+ | typeof LiteGraph.CIRCLE_SHAPE
+ | typeof LiteGraph.ARROW_SHAPE
+ | typeof LiteGraph.SQUARE_SHAPE
+ | number; // For custom shapes
+
+ /** https://github.com/jagenjo/litegraph.js/tree/master/guides#node-slots */
+ export interface INodeSlot {
+ name: string;
+ type: string | -1;
+ label?: string;
+ dir?:
+ | typeof LiteGraph.UP
+ | typeof LiteGraph.RIGHT
+ | typeof LiteGraph.DOWN
+ | typeof LiteGraph.LEFT;
+ color_on?: string;
+ color_off?: string;
+ shape?: SlotShape;
+ locked?: boolean;
+ nameLocked?: boolean;
+ }
+
+ export interface INodeInputSlot extends INodeSlot {
+ link: LLink["id"] | null;
+ }
+ export interface INodeOutputSlot extends INodeSlot {
+ links: LLink["id"][] | null;
+ }
+
+ export type WidgetCallback = (
+ this: T,
+ value: T["value"],
+ graphCanvas: LGraphCanvas,
+ node: LGraphNode,
+ pos: Vector2,
+ event?: MouseEvent
+ ) => void;
+
+ export interface IWidget {
+ name: string | null;
+ value: TValue;
+ options?: TOptions;
+ type?: widgetTypes;
+ y?: number;
+ property?: string;
+ last_y?: number;
+ clicked?: boolean;
+ marker?: boolean;
+ callback?: WidgetCallback;
+ /** Called by `LGraphCanvas.drawNodeWidgets` */
+ draw?(
+ ctx: CanvasRenderingContext2D,
+ node: LGraphNode,
+ width: number,
+ posY: number,
+ height: number
+ ): void;
+ /**
+ * Called by `LGraphCanvas.processNodeWidgets`
+ * https://github.com/jagenjo/litegraph.js/issues/76
+ */
+ mouse?(
+ event: MouseEvent,
+ pos: Vector2,
+ node: LGraphNode
+ ): boolean;
+ /** Called by `LGraphNode.computeSize` */
+ computeSize?(width: number): [number, number];
+ }
+ export interface IButtonWidget extends IWidget {
+ type: "button";
+ }
+ export interface IToggleWidget
+ extends IWidget {
+ type: "toggle";
+ }
+ export interface ISliderWidget
+ extends IWidget {
+ type: "slider";
+ }
+ export interface INumberWidget extends IWidget {
+ type: "number";
+ }
+ export interface IComboWidget
+ extends IWidget<
+ string[],
+ {
+ values:
+ | string[]
+ | ((widget: IComboWidget, node: LGraphNode) => string[]);
+ }
+ > {
+ type: "combo";
+ }
+
+ export interface ITextWidget extends IWidget {
+ type: "text";
+ }
+
+ export interface IContextMenuItem {
+ content: string;
+ callback?: ContextMenuEventListener;
+ /** Used as innerHTML for extra child element */
+ title?: string;
+ disabled?: boolean;
+ has_submenu?: boolean;
+ submenu?: {
+ options: ContextMenuItem[];
+ } & IContextMenuOptions;
+ className?: string;
+ }
+ export interface IContextMenuOptions {
+ callback?: ContextMenuEventListener;
+ ignore_item_callbacks?: Boolean;
+ event?: MouseEvent | CustomEvent;
+ parentMenu?: ContextMenu;
+ autoopen?: boolean;
+ title?: string;
+ extra?: any;
+ }
+
+ export type ContextMenuItem = IContextMenuItem | null;
+ export type ContextMenuEventListener = (
+ value: ContextMenuItem,
+ options: IContextMenuOptions,
+ event: MouseEvent,
+ parentMenu: ContextMenu | undefined,
+ node: LGraphNode
+ ) => boolean | void;
+
+ export interface LGraphNodeBase {
+ (this: LGraphNode),
+ title?: string,
+ category?: string,
+ supported_extensions?: string[]
+ }
+
+ export const LiteGraph: {
+ VERSION: number;
+
+ CANVAS_GRID_SIZE: number;
+
+ NODE_TITLE_HEIGHT: number;
+ NODE_TITLE_TEXT_Y: number;
+ NODE_SLOT_HEIGHT: number;
+ NODE_WIDGET_HEIGHT: number;
+ NODE_WIDTH: number;
+ NODE_MIN_WIDTH: number;
+ NODE_COLLAPSED_RADIUS: number;
+ NODE_COLLAPSED_WIDTH: number;
+ NODE_TITLE_COLOR: string;
+ NODE_TEXT_SIZE: number;
+ NODE_TEXT_COLOR: string;
+ NODE_SUBTEXT_SIZE: number;
+ NODE_DEFAULT_COLOR: string;
+ NODE_DEFAULT_BGCOLOR: string;
+ NODE_DEFAULT_BOXCOLOR: string;
+ NODE_DEFAULT_SHAPE: string;
+ DEFAULT_SHADOW_COLOR: string;
+ DEFAULT_GROUP_FONT: number;
+
+ LINK_COLOR: string;
+ EVENT_LINK_COLOR: string;
+ CONNECTING_LINK_COLOR: string;
+
+ MAX_NUMBER_OF_NODES: number; //avoid infinite loops
+ DEFAULT_POSITION: Vector2; //default node position
+ VALID_SHAPES: ["default", "box", "round", "card"]; //,"circle"
+
+ //shapes are used for nodes but also for slots
+ BOX_SHAPE: 1;
+ ROUND_SHAPE: 2;
+ CIRCLE_SHAPE: 3;
+ CARD_SHAPE: 4;
+ ARROW_SHAPE: 5;
+ SQUARE_SHAPE: 6;
+
+ //enums
+ INPUT: 1;
+ OUTPUT: 2;
+
+ EVENT: -1; //for outputs
+ ACTION: -1; //for inputs
+
+ ALWAYS: 0;
+ ON_EVENT: 1;
+ NEVER: 2;
+ ON_TRIGGER: 3;
+
+ UP: 1;
+ DOWN: 2;
+ LEFT: 3;
+ RIGHT: 4;
+ CENTER: 5;
+
+ STRAIGHT_LINK: 0;
+ LINEAR_LINK: 1;
+ SPLINE_LINK: 2;
+
+ NORMAL_TITLE: 0;
+ NO_TITLE: 1;
+ TRANSPARENT_TITLE: 2;
+ AUTOHIDE_TITLE: 3;
+
+ node_images_path: string;
+
+ debug: boolean;
+ catch_exceptions: boolean;
+ throw_errors: boolean;
+ /** if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits */
+ allow_scripts: boolean;
+ /** node types by string */
+ registered_node_types: Record;
+ /** used for dropping files in the canvas */
+ node_types_by_file_extension: Record;
+ /** node types by class name */
+ Nodes: Record;
+ /** used to store vars between graphs **/
+ Globals: Record;
+
+ /** used to add extra features to the search box */
+ searchbox_extras: Record<
+ string,
+ {
+ data: { outputs: string[][]; title: string };
+ desc: string;
+ type: string;
+ }
+ >;
+
+ // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action), visual feedback
+ node_box_coloured_when_on: boolean;
+ // [true!] nodebox based on node mode; visual feedback
+ node_box_coloured_by_mode: boolean;
+
+ // [false on mobile] better true if not touch device, TODO add an helper/listener to close if false
+ dialog_close_on_mouse_leave: boolean;
+ dialog_close_on_mouse_leave_delay: number;
+
+ // [false!] prefer false if results too easy to break links - implement with ALT or TODO custom keys
+ shift_click_do_break_link_from: boolean;
+ // [false!]prefer false, way too easy to break links
+ click_do_break_link_to: boolean;
+
+ // [false on mobile] better true if not touch device, TODO add an helper/listener to close if false
+ search_hide_on_mouse_leave: boolean;
+ // [true!] enable filtering slots type in the search widget, !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out]
+ search_filter_enabled: boolean;
+ // [true!] opens the results list when opening the search widget
+ search_show_all_on_open: boolean;
+
+ // [if want false, use true, run, get vars values to be statically set, than disable] nodes types and nodeclass association with node types need to be calculated, if dont want this, calculate once and set registered_slot_[in/out]_types and slot_types_[in/out]
+ auto_load_slot_types: boolean;
+
+ // slot types for nodeclass
+ registered_slot_in_types: Record }>;
+ // slot types for nodeclass
+ registered_slot_out_types: Record }>;
+ // slot types IN
+ slot_types_in: Array;
+ // slot types OUT
+ slot_types_out: Array;
+ // specify for each IN slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search
+ slot_types_default_in: Record;
+ // specify for each OUT slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search
+ slot_types_default_out: Record;
+
+ // [true!] very handy, ALT click to clone and drag the new node
+ alt_drag_do_clone_nodes: boolean;
+
+ // [true!] will create and connect event slots when using action/events connections, !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this
+ do_add_triggers_slots: boolean;
+
+ // [false!] being events, it is strongly reccomended to use them sequentially, one by one
+ allow_multi_output_for_events: boolean;
+
+ //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
+ middle_click_slot_add_default_node: boolean;
+
+ //[true!] dragging a link to empty space will open a menu, add from list, search or defaults
+ release_link_on_empty_shows_menu: boolean;
+
+ // use mouse for retrocompatibility issues? (none found @ now)
+ pointerevents_method: "mouse" | "pointer";
+
+ createNode(type: string): T;
+ /** Register a node class so it can be listed when the user wants to create a new one */
+ registerNodeType(type: string, base: LGraphNodeBase ): void;
+ /** removes a node type from the system */
+ unregisterNodeType(type: string): void;
+ /** Removes all previously registered node's types. */
+ clearRegisteredTypes(): void;
+ /**
+ * Create a new node type by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function.
+ * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output.
+ * @param name node name with namespace (p.e.: 'math/sum')
+ * @param func
+ * @param param_types an array containing the type of every parameter, otherwise parameters will accept any type
+ * @param return_type string with the return type, otherwise it will be generic
+ * @param properties properties to be configurable
+ */
+ wrapFunctionAsNode(
+ name: string,
+ func: (...args: any[]) => any,
+ param_types?: string[],
+ return_type?: string,
+ properties?: object
+ ): void;
+
+ /**
+ * Adds this method to all node types, existing and to be created
+ * (You can add it to LGraphNode.prototype but then existing node types wont have it)
+ */
+ addNodeMethod(name: string, func: (...args: any[]) => any): void;
+
+ /**
+ * Create a node of a given type with a name. The node is not attached to any graph yet.
+ * @param type full name of the node class. p.e. "math/sin"
+ * @param name a name to distinguish from other nodes
+ * @param options to set options
+ */
+ createNode(
+ type: string,
+ title: string,
+ options: object
+ ): T;
+
+ /**
+ * Returns a registered node type with a given name
+ * @param type full name of the node class. p.e. "math/sin"
+ */
+ getNodeType(type: string): LGraphNodeConstructor;
+
+ /**
+ * Returns a list of node types matching one category
+ * @method getNodeTypesInCategory
+ * @param {String} category category name
+ * @param {String} filter only nodes with ctor.filter equal can be shown
+ * @return {Array} array with all the node classes
+ */
+ getNodeTypesInCategory(
+ category: string,
+ filter: string
+ ): LGraphNodeConstructor[];
+
+ /**
+ * Returns a list with all the node type categories
+ * @method getNodeTypesCategories
+ * @param {String} filter only nodes with ctor.filter equal can be shown
+ * @return {Array} array with all the names of the categories
+ */
+ getNodeTypesCategories(filter: string): string[];
+
+ /** debug purposes: reloads all the js scripts that matches a wildcard */
+ reloadNodes(folder_wildcard: string): void;
+
+ getTime(): number;
+ LLink: typeof LLink;
+ LGraph: typeof LGraph;
+ DragAndScale: typeof DragAndScale;
+ compareObjects(a: object, b: object): boolean;
+ distance(a: Vector2, b: Vector2): number;
+ colorToString(c: string): string;
+ isInsideRectangle(
+ x: number,
+ y: number,
+ left: number,
+ top: number,
+ width: number,
+ height: number
+ ): boolean;
+ growBounding(bounding: Vector4, x: number, y: number): Vector4;
+ isInsideBounding(p: Vector2, bb: Vector4): boolean;
+ hex2num(hex: string): [number, number, number];
+ num2hex(triplet: [number, number, number]): string;
+ ContextMenu: typeof ContextMenu;
+ extendClass(target: A, origin: B): A & B;
+ getParameterNames(func: string): string[];
+ };
+
+ export type serializedLGraph<
+ TNode = ReturnType,
+ // https://github.com/jagenjo/litegraph.js/issues/74
+ TLink = [number, number, number, number, number, string],
+ TGroup = ReturnType
+ > = {
+ last_node_id: LGraph["last_node_id"];
+ last_link_id: LGraph["last_link_id"];
+ nodes: TNode[];
+ links: TLink[];
+ groups: TGroup[];
+ config: LGraph["config"];
+ version: typeof LiteGraph.VERSION;
+ };
+
+ export declare class LGraph {
+ static supported_types: string[];
+ static STATUS_STOPPED: 1;
+ static STATUS_RUNNING: 2;
+
+ constructor(o?: object);
+
+ filter: string;
+ catch_errors: boolean;
+ /** custom data */
+ config: object;
+ elapsed_time: number;
+ fixedtime: number;
+ fixedtime_lapse: number;
+ globaltime: number;
+ inputs: any;
+ iteration: number;
+ last_link_id: number;
+ last_node_id: number;
+ last_update_time: number;
+ links: Record;
+ list_of_graphcanvas: LGraphCanvas[];
+ outputs: any;
+ runningtime: number;
+ starttime: number;
+ status: typeof LGraph.STATUS_RUNNING | typeof LGraph.STATUS_STOPPED;
+
+ /* private */ _nodes: LGraphNode[];
+ private _groups: LGraphGroup[];
+ private _nodes_by_id: Record;
+ /** nodes that are executable sorted in execution order */
+ private _nodes_executable:
+ | (LGraphNode & { onExecute: NonNullable }[])
+ | null;
+ /** nodes that contain onExecute */
+ private _nodes_in_order: LGraphNode[];
+ private _version: number;
+
+ getSupportedTypes(): string[];
+ /** Removes all nodes from this graph */
+ clear(): void;
+ /** Attach Canvas to this graph */
+ attachCanvas(graphCanvas: LGraphCanvas): void;
+ /** Detach Canvas to this graph */
+ detachCanvas(graphCanvas: LGraphCanvas): void;
+ /**
+ * Starts running this graph every interval milliseconds.
+ * @param interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate
+ */
+ start(interval?: number): void;
+ /** Stops the execution loop of the graph */
+ stop(): void;
+ /**
+ * Run N steps (cycles) of the graph
+ * @param num number of steps to run, default is 1
+ */
+ runStep(num?: number, do_not_catch_errors?: boolean): void;
+ /**
+ * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than
+ * nodes with only inputs.
+ */
+ updateExecutionOrder(): void;
+ /** This is more internal, it computes the executable nodes in order and returns it */
+ computeExecutionOrder(only_onExecute: boolean, set_level: any): T;
+ /**
+ * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively.
+ * It doesn't include the node itself
+ * @return an array with all the LGraphNodes that affect this node, in order of execution
+ */
+ getAncestors(node: LGraphNode): LGraphNode[];
+ /**
+ * Positions every node in a more readable manner
+ */
+ arrange(margin?: number,layout?: string): void;
+ /**
+ * Returns the amount of time the graph has been running in milliseconds
+ * @return number of milliseconds the graph has been running
+ */
+ getTime(): number;
+
+ /**
+ * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant
+ * @return number of milliseconds the graph has been running
+ */
+ getFixedTime(): number;
+
+ /**
+ * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct
+ * if the nodes are using graphical actions
+ * @return number of milliseconds it took the last cycle
+ */
+ getElapsedTime(): number;
+ /**
+ * Sends an event to all the nodes, useful to trigger stuff
+ * @param eventName the name of the event (function to be called)
+ * @param params parameters in array format
+ */
+ sendEventToAllNodes(eventName: string, params: any[], mode?: any): void;
+
+ sendActionToCanvas(action: any, params: any[]): void;
+ /**
+ * Adds a new node instance to this graph
+ * @param node the instance of the node
+ */
+ add(node: LGraphNode, skip_compute_order?: boolean): void;
+ /**
+ * Called when a new node is added
+ * @param node the instance of the node
+ */
+ onNodeAdded(node: LGraphNode): void;
+ /** Removes a node from the graph */
+ remove(node: LGraphNode): void;
+ /** Returns a node by its id. */
+ getNodeById(id: number): LGraphNode | undefined;
+ /**
+ * Returns a list of nodes that matches a class
+ * @param classObject the class itself (not an string)
+ * @return a list with all the nodes of this type
+ */
+ findNodesByClass(
+ classObject: LGraphNodeConstructor
+ ): T[];
+ /**
+ * Returns a list of nodes that matches a type
+ * @param type the name of the node type
+ * @return a list with all the nodes of this type
+ */
+ findNodesByType(type: string): T[];
+ /**
+ * Returns the first node that matches a name in its title
+ * @param title the name of the node to search
+ * @return the node or null
+ */
+ findNodeByTitle(title: string): T | null;
+ /**
+ * Returns a list of nodes that matches a name
+ * @param title the name of the node to search
+ * @return a list with all the nodes with this name
+ */
+ findNodesByTitle(title: string): T[];
+ /**
+ * Returns the top-most node in this position of the canvas
+ * @param x the x coordinate in canvas space
+ * @param y the y coordinate in canvas space
+ * @param nodes_list a list with all the nodes to search from, by default is all the nodes in the graph
+ * @return the node at this position or null
+ */
+ getNodeOnPos(
+ x: number,
+ y: number,
+ node_list?: LGraphNode[],
+ margin?: number
+ ): T | null;
+ /**
+ * Returns the top-most group in that position
+ * @param x the x coordinate in canvas space
+ * @param y the y coordinate in canvas space
+ * @return the group or null
+ */
+ getGroupOnPos(x: number, y: number): LGraphGroup | null;
+
+ onAction(action: any, param: any): void;
+ trigger(action: any, param: any): void;
+ /** Tell this graph it has a global graph input of this type */
+ addInput(name: string, type: string, value?: any): void;
+ /** Assign a data to the global graph input */
+ setInputData(name: string, data: any): void;
+ /** Returns the current value of a global graph input */
+ getInputData(name: string): T;
+ /** Changes the name of a global graph input */
+ renameInput(old_name: string, name: string): false | undefined;
+ /** Changes the type of a global graph input */
+ changeInputType(name: string, type: string): false | undefined;
+ /** Removes a global graph input */
+ removeInput(name: string): boolean;
+ /** Creates a global graph output */
+ addOutput(name: string, type: string, value: any): void;
+ /** Assign a data to the global output */
+ setOutputData(name: string, value: string): void;
+ /** Returns the current value of a global graph output */
+ getOutputData(name: string): T;
+
+ /** Renames a global graph output */
+ renameOutput(old_name: string, name: string): false | undefined;
+ /** Changes the type of a global graph output */
+ changeOutputType(name: string, type: string): false | undefined;
+ /** Removes a global graph output */
+ removeOutput(name: string): boolean;
+ triggerInput(name: string, value: any): void;
+ setCallback(name: string, func: (...args: any[]) => any): void;
+ beforeChange(info?: LGraphNode): void;
+ afterChange(info?: LGraphNode): void;
+ connectionChange(node: LGraphNode): void;
+ /** returns if the graph is in live mode */
+ isLive(): boolean;
+ /** clears the triggered slot animation in all links (stop visual animation) */
+ clearTriggeredSlots(): void;
+ /* Called when something visually changed (not the graph!) */
+ change(): void;
+ setDirtyCanvas(fg: boolean, bg: boolean): void;
+ /** Destroys a link */
+ removeLink(link_id: number): void;
+ /** Creates a Object containing all the info about this graph, it can be serialized */
+ serialize(): T;
+ /**
+ * Configure a graph from a JSON string
+ * @param data configure a graph from a JSON string
+ * @returns if there was any error parsing
+ */
+ configure(data: object, keep_old?: boolean): boolean | undefined;
+ load(url: string): void;
+ }
+
+ export type SerializedLLink = [number, string, number, number, number, number];
+ export declare class LLink {
+ id: number;
+ type: string;
+ origin_id: number;
+ origin_slot: number;
+ target_id: number;
+ target_slot: number;
+ constructor(
+ id: number,
+ type: string,
+ origin_id: number,
+ origin_slot: number,
+ target_id: number,
+ target_slot: number
+ );
+ configure(o: LLink | SerializedLLink): void;
+ serialize(): SerializedLLink;
+ }
+
+ export type SerializedLGraphNode = {
+ id: T["id"];
+ type: T["type"];
+ pos: T["pos"];
+ size: T["size"];
+ flags: T["flags"];
+ mode: T["mode"];
+ inputs: T["inputs"];
+ outputs: T["outputs"];
+ title: T["title"];
+ properties: T["properties"];
+ widgets_values?: IWidget["value"][];
+ };
+
+ /** https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#lgraphnode */
+ export declare class LGraphNode {
+ static title_color: string;
+ static title: string;
+ static type: null | string;
+ static widgets_up: boolean;
+ constructor(title?: string);
+
+ title: string;
+ type: null | string;
+ category: null | string;
+ size: Vector2;
+ graph: null | LGraph;
+ graph_version: number;
+ pos: Vector2;
+ is_selected: boolean;
+ mouseOver: boolean;
+
+ id: number;
+
+ widgets: IWidget[] | null | undefined;
+
+ //inputs available: array of inputs
+ inputs: INodeInputSlot[];
+ outputs: INodeOutputSlot[];
+ connections: any[];
+
+ //local data
+ properties: Record;
+ properties_info: any[];
+
+ flags: Partial<{
+ collapsed: boolean
+ }>;
+
+ color: string;
+ bgcolor: string;
+ boxcolor: string;
+ shape:
+ | typeof LiteGraph.BOX_SHAPE
+ | typeof LiteGraph.ROUND_SHAPE
+ | typeof LiteGraph.CIRCLE_SHAPE
+ | typeof LiteGraph.CARD_SHAPE
+ | typeof LiteGraph.ARROW_SHAPE;
+
+ serialize_widgets: boolean;
+ skip_list: boolean;
+
+ /** Used in `LGraphCanvas.onMenuNodeMode` */
+ mode?:
+ | typeof LiteGraph.ON_EVENT
+ | typeof LiteGraph.ON_TRIGGER
+ | typeof LiteGraph.NEVER
+ | typeof LiteGraph.ALWAYS;
+
+ /** If set to true widgets do not start after the slots */
+ widgets_up: boolean;
+ /** widgets start at y distance from the top of the node */
+ widgets_start_y: number;
+ /** if you render outside the node, it will be clipped */
+ clip_area: boolean;
+ /** if set to false it wont be resizable with the mouse */
+ resizable: boolean;
+ /** slots are distributed horizontally */
+ horizontal: boolean;
+ /** if true, the node will show the bgcolor as 'red' */
+ has_errors?: boolean;
+
+ /** configure a node from an object containing the serialized info */
+ configure(info: SerializedLGraphNode): void;
+ /** serialize the content */
+ serialize(): SerializedLGraphNode;
+ /** Creates a clone of this node */
+ clone(): this;
+ /** serialize and stringify */
+ toString(): string;
+ /** get the title string */
+ getTitle(): string;
+ /** sets the value of a property */
+ setProperty(name: string, value: any): void;
+ /** sets the output data */
+ setOutputData(slot: number, data: any): void;
+ /** sets the output data */
+ setOutputDataType(slot: number, type: string): void;
+ /**
+ * Retrieves the input data (data traveling through the connection) from one slot
+ * @param slot
+ * @param force_update if set to true it will force the connected node of this slot to output data into this link
+ * @return data or if it is not connected returns undefined
+ */
+ getInputData(slot: number, force_update?: boolean): T;
+ /**
+ * Retrieves the input data type (in case this supports multiple input types)
+ * @param slot
+ * @return datatype in string format
+ */
+ getInputDataType(slot: number): string;
+ /**
+ * Retrieves the input data from one slot using its name instead of slot number
+ * @param slot_name
+ * @param force_update if set to true it will force the connected node of this slot to output data into this link
+ * @return data or if it is not connected returns null
+ */
+ getInputDataByName(slot_name: string, force_update?: boolean): T;
+ /** tells you if there is a connection in one input slot */
+ isInputConnected(slot: number): boolean;
+ /** tells you info about an input connection (which node, type, etc) */
+ getInputInfo(
+ slot: number
+ ): { link: number; name: string; type: string | 0 } | null;
+ /** returns the node connected in the input slot */
+ getInputNode(slot: number): LGraphNode | null;
+ /** returns the value of an input with this name, otherwise checks if there is a property with that name */
+ getInputOrProperty(name: string): T;
+ /** tells you the last output data that went in that slot */
+ getOutputData(slot: number): T | null;
+ /** tells you info about an output connection (which node, type, etc) */
+ getOutputInfo(
+ slot: number
+ ): { name: string; type: string; links: number[] } | null;
+ /** tells you if there is a connection in one output slot */
+ isOutputConnected(slot: number): boolean;
+ /** tells you if there is any connection in the output slots */
+ isAnyOutputConnected(): boolean;
+ /** retrieves all the nodes connected to this output slot */
+ getOutputNodes(slot: number): LGraphNode[];
+ /** Triggers an event in this node, this will trigger any output with the same name */
+ trigger(action: string, param: any): void;
+ /**
+ * Triggers an slot event in this node
+ * @param slot the index of the output slot
+ * @param param
+ * @param link_id in case you want to trigger and specific output link in a slot
+ */
+ triggerSlot(slot: number, param: any, link_id?: number): void;
+ /**
+ * clears the trigger slot animation
+ * @param slot the index of the output slot
+ * @param link_id in case you want to trigger and specific output link in a slot
+ */
+ clearTriggeredSlot(slot: number, link_id?: number): void;
+ /**
+ * add a new property to this node
+ * @param name
+ * @param default_value
+ * @param type string defining the output type ("vec3","number",...)
+ * @param extra_info this can be used to have special properties of the property (like values, etc)
+ */
+ addProperty(
+ name: string,
+ default_value: any,
+ type: string,
+ extra_info?: object
+ ): T;
+ /**
+ * add a new output slot to use in this node
+ * @param name
+ * @param type string defining the output type ("vec3","number",...)
+ * @param extra_info this can be used to have special properties of an output (label, special color, position, etc)
+ */
+ addOutput(
+ name: string,
+ type: string | -1,
+ extra_info?: Partial
+ ): INodeOutputSlot;
+ /**
+ * add a new output slot to use in this node
+ * @param array of triplets like [[name,type,extra_info],[...]]
+ */
+ addOutputs(
+ array: [string, string | -1, Partial | undefined][]
+ ): void;
+ /** remove an existing output slot */
+ removeOutput(slot: number): void;
+ /**
+ * add a new input slot to use in this node
+ * @param name
+ * @param type string defining the input type ("vec3","number",...), it its a generic one use 0
+ * @param extra_info this can be used to have special properties of an input (label, color, position, etc)
+ */
+ addInput(
+ name: string,
+ type: string | -1,
+ extra_info?: Partial
+ ): INodeInputSlot;
+ /**
+ * add several new input slots in this node
+ * @param array of triplets like [[name,type,extra_info],[...]]
+ */
+ addInputs(
+ array: [string, string | -1, Partial | undefined][]
+ ): void;
+ /** remove an existing input slot */
+ removeInput(slot: number): void;
+ /**
+ * add an special connection to this node (used for special kinds of graphs)
+ * @param name
+ * @param type string defining the input type ("vec3","number",...)
+ * @param pos position of the connection inside the node
+ * @param direction if is input or output
+ */
+ addConnection(
+ name: string,
+ type: string,
+ pos: Vector2,
+ direction: string
+ ): {
+ name: string;
+ type: string;
+ pos: Vector2;
+ direction: string;
+ links: null;
+ };
+ setValue(v: any): void;
+ /** computes the size of a node according to its inputs and output slots */
+ computeSize(): [number, number];
+ /**
+ * https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#node-widgets
+ * @return created widget
+ */
+ addWidget(
+ type: T["type"],
+ name: string,
+ value: T["value"],
+ callback?: WidgetCallback | string,
+ options?: T["options"]
+ ): T;
+
+ addCustomWidget(customWidget: T): T;
+
+ /**
+ * returns the bounding of the object, used for rendering purposes
+ * @return [x, y, width, height]
+ */
+ getBounding(): Vector4;
+ /** checks if a point is inside the shape of a node */
+ isPointInside(
+ x: number,
+ y: number,
+ margin?: number,
+ skipTitle?: boolean
+ ): boolean;
+ /** checks if a point is inside a node slot, and returns info about which slot */
+ getSlotInPosition(
+ x: number,
+ y: number
+ ): {
+ input?: INodeInputSlot;
+ output?: INodeOutputSlot;
+ slot: number;
+ link_pos: Vector2;
+ };
+ /**
+ * returns the input slot with a given name (used for dynamic slots), -1 if not found
+ * @param name the name of the slot
+ * @return the slot (-1 if not found)
+ */
+ findInputSlot(name: string): number;
+ /**
+ * returns the output slot with a given name (used for dynamic slots), -1 if not found
+ * @param name the name of the slot
+ * @return the slot (-1 if not found)
+ */
+ findOutputSlot(name: string): number;
+ /**
+ * connect this node output to the input of another node
+ * @param slot (could be the number of the slot or the string with the name of the slot)
+ * @param targetNode the target node
+ * @param targetSlot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)
+ * @return {Object} the link_info is created, otherwise null
+ */
+ connect(
+ slot: number | string,
+ targetNode: LGraphNode,
+ targetSlot: number | string
+ ): T | null;
+ /**
+ * disconnect one output to an specific node
+ * @param slot (could be the number of the slot or the string with the name of the slot)
+ * @param target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]
+ * @return if it was disconnected successfully
+ */
+ disconnectOutput(slot: number | string, targetNode?: LGraphNode): boolean;
+ /**
+ * disconnect one input
+ * @param slot (could be the number of the slot or the string with the name of the slot)
+ * @return if it was disconnected successfully
+ */
+ disconnectInput(slot: number | string): boolean;
+ /**
+ * returns the center of a connection point in canvas coords
+ * @param is_input true if if a input slot, false if it is an output
+ * @param slot (could be the number of the slot or the string with the name of the slot)
+ * @param out a place to store the output, to free garbage
+ * @return the position
+ **/
+ getConnectionPos(
+ is_input: boolean,
+ slot: number | string,
+ out?: Vector2
+ ): Vector2;
+ /** Force align to grid */
+ alignToGrid(): void;
+ /** Console output */
+ trace(msg: string): void;
+ /** Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */
+ setDirtyCanvas(fg: boolean, bg: boolean): void;
+ loadImage(url: string): void;
+ /** Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */
+ captureInput(v: any): void;
+ /** Collapse the node to make it smaller on the canvas */
+ collapse(force: boolean): void;
+ /** Forces the node to do not move or realign on Z */
+ pin(v?: boolean): void;
+ localToScreen(x: number, y: number, graphCanvas: LGraphCanvas): Vector2;
+
+ // https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#custom-node-appearance
+ onDrawBackground?(
+ ctx: CanvasRenderingContext2D,
+ canvas: HTMLCanvasElement
+ ): void;
+ onDrawForeground?(
+ ctx: CanvasRenderingContext2D,
+ canvas: HTMLCanvasElement
+ ): void;
+
+ // https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#custom-node-behaviour
+ onMouseDown?(
+ event: MouseEvent,
+ pos: Vector2,
+ graphCanvas: LGraphCanvas
+ ): void;
+ onMouseMove?(
+ event: MouseEvent,
+ pos: Vector2,
+ graphCanvas: LGraphCanvas
+ ): void;
+ onMouseUp?(
+ event: MouseEvent,
+ pos: Vector2,
+ graphCanvas: LGraphCanvas
+ ): void;
+ onMouseEnter?(
+ event: MouseEvent,
+ pos: Vector2,
+ graphCanvas: LGraphCanvas
+ ): void;
+ onMouseLeave?(
+ event: MouseEvent,
+ pos: Vector2,
+ graphCanvas: LGraphCanvas
+ ): void;
+ onKey?(event: KeyboardEvent, pos: Vector2, graphCanvas: LGraphCanvas): void;
+
+ /** Called by `LGraphCanvas.selectNodes` */
+ onSelected?(): void;
+ /** Called by `LGraphCanvas.deselectNode` */
+ onDeselected?(): void;
+ /** Called by `LGraph.runStep` `LGraphNode.getInputData` */
+ onExecute?(): void;
+ /** Called by `LGraph.serialize` */
+ onSerialize?(o: SerializedLGraphNode): void;
+ /** Called by `LGraph.configure` */
+ onConfigure?(o: SerializedLGraphNode): void;
+ /**
+ * when added to graph (warning: this is called BEFORE the node is configured when loading)
+ * Called by `LGraph.add`
+ */
+ onAdded?(graph: LGraph): void;
+ /**
+ * when removed from graph
+ * Called by `LGraph.remove` `LGraph.clear`
+ */
+ onRemoved?(): void;
+ /**
+ * if returns false the incoming connection will be canceled
+ * Called by `LGraph.connect`
+ * @param inputIndex target input slot number
+ * @param outputType type of output slot
+ * @param outputSlot output slot object
+ * @param outputNode node containing the output
+ * @param outputIndex index of output slot
+ */
+ onConnectInput?(
+ inputIndex: number,
+ outputType: INodeOutputSlot["type"],
+ outputSlot: INodeOutputSlot,
+ outputNode: LGraphNode,
+ outputIndex: number
+ ): boolean;
+ /**
+ * if returns false the incoming connection will be canceled
+ * Called by `LGraph.connect`
+ * @param outputIndex target output slot number
+ * @param inputType type of input slot
+ * @param inputSlot input slot object
+ * @param inputNode node containing the input
+ * @param inputIndex index of input slot
+ */
+ onConnectOutput?(
+ outputIndex: number,
+ inputType: INodeInputSlot["type"],
+ inputSlot: INodeInputSlot,
+ inputNode: LGraphNode,
+ inputIndex: number
+ ): boolean;
+
+ /**
+ * Called just before connection (or disconnect - if input is linked).
+ * A convenient place to switch to another input, or create new one.
+ * This allow for ability to automatically add slots if needed
+ * @param inputIndex
+ * @return selected input slot index, can differ from parameter value
+ */
+ onBeforeConnectInput?(
+ inputIndex: number
+ ): number;
+
+ /** a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info or output_info ) */
+ onConnectionsChange(
+ type: number,
+ slotIndex: number,
+ isConnected: boolean,
+ link: LLink,
+ ioSlot: (INodeOutputSlot | INodeInputSlot)
+ ): void;
+
+ /**
+ * if returns false, will abort the `LGraphNode.setProperty`
+ * Called when a property is changed
+ * @param property
+ * @param value
+ * @param prevValue
+ */
+ onPropertyChanged?(property: string, value: any, prevValue: any): void | boolean;
+
+ /** Called by `LGraphCanvas.processContextMenu` */
+ getMenuOptions?(graphCanvas: LGraphCanvas): ContextMenuItem[];
+ getSlotMenuOptions?(slot: INodeSlot): ContextMenuItem[];
+ }
+
+ export type LGraphNodeConstructor = {
+ new (): T;
+ };
+
+ export type SerializedLGraphGroup = {
+ title: LGraphGroup["title"];
+ bounding: LGraphGroup["_bounding"];
+ color: LGraphGroup["color"];
+ font: LGraphGroup["font"];
+ };
+ export declare class LGraphGroup {
+ title: string;
+ private _bounding: Vector4;
+ color: string;
+ font: string;
+
+ configure(o: SerializedLGraphGroup): void;
+ serialize(): SerializedLGraphGroup;
+ move(deltaX: number, deltaY: number, ignoreNodes?: boolean): void;
+ recomputeInsideNodes(): void;
+ isPointInside: LGraphNode["isPointInside"];
+ setDirtyCanvas: LGraphNode["setDirtyCanvas"];
+ }
+
+ export declare class DragAndScale {
+ constructor(element?: HTMLElement, skipEvents?: boolean);
+ offset: [number, number];
+ scale: number;
+ max_scale: number;
+ min_scale: number;
+ onredraw: Function | null;
+ enabled: boolean;
+ last_mouse: Vector2;
+ element: HTMLElement | null;
+ visible_area: Vector4;
+ bindEvents(element: HTMLElement): void;
+ computeVisibleArea(): void;
+ onMouse(e: MouseEvent): void;
+ toCanvasContext(ctx: CanvasRenderingContext2D): void;
+ convertOffsetToCanvas(pos: Vector2): Vector2;
+ convertCanvasToOffset(pos: Vector2): Vector2;
+ mouseDrag(x: number, y: number): void;
+ changeScale(value: number, zooming_center?: Vector2): void;
+ changeDeltaScale(value: number, zooming_center?: Vector2): void;
+ reset(): void;
+ }
+
+ /**
+ * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required.
+ * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked
+ *
+ * @param canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself)
+ * @param graph
+ * @param options { skip_rendering, autoresize }
+ */
+ export declare class LGraphCanvas {
+ static node_colors: Record<
+ string,
+ {
+ color: string;
+ bgcolor: string;
+ groupcolor: string;
+ }
+ >;
+ static link_type_colors: Record;
+ static gradients: object;
+ static search_limit: number;
+
+ static getFileExtension(url: string): string;
+ static decodeHTML(str: string): string;
+
+ static onMenuCollapseAll(): void;
+ static onMenuNodeEdit(): void;
+ static onShowPropertyEditor(
+ item: any,
+ options: any,
+ e: any,
+ menu: any,
+ node: any
+ ): void;
+ /** Create menu for `Add Group` */
+ static onGroupAdd: ContextMenuEventListener;
+ /** Create menu for `Add Node` */
+ static onMenuAdd: ContextMenuEventListener;
+ static showMenuNodeOptionalInputs: ContextMenuEventListener;
+ static showMenuNodeOptionalOutputs: ContextMenuEventListener;
+ static onShowMenuNodeProperties: ContextMenuEventListener;
+ static onResizeNode: ContextMenuEventListener;
+ static onMenuNodeCollapse: ContextMenuEventListener;
+ static onMenuNodePin: ContextMenuEventListener;
+ static onMenuNodeMode: ContextMenuEventListener;
+ static onMenuNodeColors: ContextMenuEventListener;
+ static onMenuNodeShapes: ContextMenuEventListener;
+ static onMenuNodeRemove: ContextMenuEventListener;
+ static onMenuNodeClone: ContextMenuEventListener;
+
+ constructor(
+ canvas: HTMLCanvasElement | string,
+ graph?: LGraph,
+ options?: {
+ skip_render?: boolean;
+ autoresize?: boolean;
+ }
+ );
+
+ static active_canvas: HTMLCanvasElement;
+
+ allow_dragcanvas: boolean;
+ allow_dragnodes: boolean;
+ /** allow to control widgets, buttons, collapse, etc */
+ allow_interaction: boolean;
+ /** allows to change a connection with having to redo it again */
+ allow_reconnect_links: boolean;
+ /** allow selecting multi nodes without pressing extra keys */
+ multi_select: boolean;
+ /** No effect */
+ allow_searchbox: boolean;
+ always_render_background: boolean;
+ autoresize?: boolean;
+ background_image: string;
+ bgcanvas: HTMLCanvasElement;
+ bgctx: CanvasRenderingContext2D;
+ canvas: HTMLCanvasElement;
+ canvas_mouse: Vector2;
+ clear_background: boolean;
+ connecting_node: LGraphNode | null;
+ connections_width: number;
+ ctx: CanvasRenderingContext2D;
+ current_node: LGraphNode | null;
+ default_connection_color: {
+ input_off: string;
+ input_on: string;
+ output_off: string;
+ output_on: string;
+ };
+ default_link_color: string;
+ dirty_area: Vector4 | null;
+ dirty_bgcanvas?: boolean;
+ dirty_canvas?: boolean;
+ drag_mode: boolean;
+ dragging_canvas: boolean;
+ dragging_rectangle: Vector4 | null;
+ ds: DragAndScale;
+ /** used for transition */
+ editor_alpha: number;
+ filter: any;
+ fps: number;
+ frame: number;
+ graph: LGraph;
+ highlighted_links: Record;
+ highquality_render: boolean;
+ inner_text_font: string;
+ is_rendering: boolean;
+ last_draw_time: number;
+ last_mouse: Vector2;
+ /**
+ * Possible duplicated with `last_mouse`
+ * https://github.com/jagenjo/litegraph.js/issues/70
+ */
+ last_mouse_position: Vector2;
+ /** Timestamp of last mouse click, defaults to 0 */
+ last_mouseclick: number;
+ links_render_mode:
+ | typeof LiteGraph.STRAIGHT_LINK
+ | typeof LiteGraph.LINEAR_LINK
+ | typeof LiteGraph.SPLINE_LINK;
+ live_mode: boolean;
+ node_capturing_input: LGraphNode | null;
+ node_dragged: LGraphNode | null;
+ node_in_panel: LGraphNode | null;
+ node_over: LGraphNode | null;
+ node_title_color: string;
+ node_widget: [LGraphNode, IWidget] | null;
+ /** Called by `LGraphCanvas.drawBackCanvas` */
+ onDrawBackground:
+ | ((ctx: CanvasRenderingContext2D, visibleArea: Vector4) => void)
+ | null;
+ /** Called by `LGraphCanvas.drawFrontCanvas` */
+ onDrawForeground:
+ | ((ctx: CanvasRenderingContext2D, visibleArea: Vector4) => void)
+ | null;
+ onDrawOverlay: ((ctx: CanvasRenderingContext2D) => void) | null;
+ /** Called by `LGraphCanvas.processMouseDown` */
+ onMouse: ((event: MouseEvent) => boolean) | null;
+ /** Called by `LGraphCanvas.drawFrontCanvas` and `LGraphCanvas.drawLinkTooltip` */
+ onDrawLinkTooltip: ((ctx: CanvasRenderingContext2D, link: LLink, _this: this) => void) | null;
+ /** Called by `LGraphCanvas.selectNodes` */
+ onNodeMoved: ((node: LGraphNode) => void) | null;
+ /** Called by `LGraphCanvas.processNodeSelected` */
+ onNodeSelected: ((node: LGraphNode) => void) | null;
+ /** Called by `LGraphCanvas.deselectNode` */
+ onNodeDeselected: ((node: LGraphNode) => void) | null;
+ /** Called by `LGraphCanvas.processNodeDblClicked` */
+ onShowNodePanel: ((node: LGraphNode) => void) | null;
+ /** Called by `LGraphCanvas.processNodeDblClicked` */
+ onNodeDblClicked: ((node: LGraphNode) => void) | null;
+ /** Called by `LGraphCanvas.selectNodes` */
+ onSelectionChange: ((nodes: Record) => void) | null;
+ /** Called by `LGraphCanvas.showSearchBox` */
+ onSearchBox:
+ | ((
+ helper: Element,
+ value: string,
+ graphCanvas: LGraphCanvas
+ ) => string[])
+ | null;
+ onSearchBoxSelection:
+ | ((name: string, event: MouseEvent, graphCanvas: LGraphCanvas) => void)
+ | null;
+ pause_rendering: boolean;
+ render_canvas_border: boolean;
+ render_collapsed_slots: boolean;
+ render_connection_arrows: boolean;
+ render_connections_border: boolean;
+ render_connections_shadows: boolean;
+ render_curved_connections: boolean;
+ render_execution_order: boolean;
+ render_only_selected: boolean;
+ render_shadows: boolean;
+ render_title_colored: boolean;
+ round_radius: number;
+ selected_group: null | LGraphGroup;
+ selected_group_resizing: boolean;
+ selected_nodes: Record;
+ show_info: boolean;
+ title_text_font: string;
+ /** set to true to render title bar with gradients */
+ use_gradients: boolean;
+ visible_area: DragAndScale["visible_area"];
+ visible_links: LLink[];
+ visible_nodes: LGraphNode[];
+ zoom_modify_alpha: boolean;
+
+ /** clears all the data inside */
+ clear(): void;
+ /** assigns a graph, you can reassign graphs to the same canvas */
+ setGraph(graph: LGraph, skipClear?: boolean): void;
+ /** opens a graph contained inside a node in the current graph */
+ openSubgraph(graph: LGraph): void;
+ /** closes a subgraph contained inside a node */
+ closeSubgraph(): void;
+ /** assigns a canvas */
+ setCanvas(canvas: HTMLCanvasElement, skipEvents?: boolean): void;
+ /** binds mouse, keyboard, touch and drag events to the canvas */
+ bindEvents(): void;
+ /** unbinds mouse events from the canvas */
+ unbindEvents(): void;
+
+ /**
+ * this function allows to render the canvas using WebGL instead of Canvas2D
+ * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL
+ **/
+ enableWebGL(): void;
+
+ /**
+ * marks as dirty the canvas, this way it will be rendered again
+ * @param fg if the foreground canvas is dirty (the one containing the nodes)
+ * @param bg if the background canvas is dirty (the one containing the wires)
+ */
+ setDirty(fg: boolean, bg: boolean): void;
+
+ /**
+ * Used to attach the canvas in a popup
+ * @return the window where the canvas is attached (the DOM root node)
+ */
+ getCanvasWindow(): Window;
+ /** starts rendering the content of the canvas when needed */
+ startRendering(): void;
+ /** stops rendering the content of the canvas (to save resources) */
+ stopRendering(): void;
+
+ processMouseDown(e: MouseEvent): boolean | undefined;
+ processMouseMove(e: MouseEvent): boolean | undefined;
+ processMouseUp(e: MouseEvent): boolean | undefined;
+ processMouseWheel(e: MouseEvent): boolean | undefined;
+
+ /** returns true if a position (in graph space) is on top of a node little corner box */
+ isOverNodeBox(node: LGraphNode, canvasX: number, canvasY: number): boolean;
+ /** returns true if a position (in graph space) is on top of a node input slot */
+ isOverNodeInput(
+ node: LGraphNode,
+ canvasX: number,
+ canvasY: number,
+ slotPos: Vector2
+ ): boolean;
+
+ /** process a key event */
+ processKey(e: KeyboardEvent): boolean | undefined;
+
+ copyToClipboard(): void;
+ pasteFromClipboard(): void;
+ processDrop(e: DragEvent): void;
+ checkDropItem(e: DragEvent): void;
+ processNodeDblClicked(n: LGraphNode): void;
+ processNodeSelected(n: LGraphNode, e: MouseEvent): void;
+ processNodeDeselected(node: LGraphNode): void;
+
+ /** selects a given node (or adds it to the current selection) */
+ selectNode(node: LGraphNode, add?: boolean): void;
+ /** selects several nodes (or adds them to the current selection) */
+ selectNodes(nodes?: LGraphNode[], add?: boolean): void;
+ /** removes a node from the current selection */
+ deselectNode(node: LGraphNode): void;
+ /** removes all nodes from the current selection */
+ deselectAllNodes(): void;
+ /** deletes all nodes in the current selection from the graph */
+ deleteSelectedNodes(): void;
+
+ /** centers the camera on a given node */
+ centerOnNode(node: LGraphNode): void;
+ /** changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom */
+ setZoom(value: number, center: Vector2): void;
+ /** brings a node to front (above all other nodes) */
+ bringToFront(node: LGraphNode): void;
+ /** sends a node to the back (below all other nodes) */
+ sendToBack(node: LGraphNode): void;
+ /** checks which nodes are visible (inside the camera area) */
+ computeVisibleNodes(nodes: LGraphNode[]): LGraphNode[];
+ /** renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes) */
+ draw(forceFG?: boolean, forceBG?: boolean): void;
+ /** draws the front canvas (the one containing all the nodes) */
+ drawFrontCanvas(): void;
+ /** draws some useful stats in the corner of the canvas */
+ renderInfo(ctx: CanvasRenderingContext2D, x: number, y: number): void;
+ /** draws the back canvas (the one containing the background and the connections) */
+ drawBackCanvas(): void;
+ /** draws the given node inside the canvas */
+ drawNode(node: LGraphNode, ctx: CanvasRenderingContext2D): void;
+ /** draws graphic for node's slot */
+ drawSlotGraphic(ctx: CanvasRenderingContext2D, pos: number[], shape: SlotShape, horizontal: boolean): void;
+ /** draws the shape of the given node in the canvas */
+ drawNodeShape(
+ node: LGraphNode,
+ ctx: CanvasRenderingContext2D,
+ size: [number, number],
+ fgColor: string,
+ bgColor: string,
+ selected: boolean,
+ mouseOver: boolean
+ ): void;
+ /** draws every connection visible in the canvas */
+ drawConnections(ctx: CanvasRenderingContext2D): void;
+ /**
+ * draws a link between two points
+ * @param a start pos
+ * @param b end pos
+ * @param link the link object with all the link info
+ * @param skipBorder ignore the shadow of the link
+ * @param flow show flow animation (for events)
+ * @param color the color for the link
+ * @param startDir the direction enum
+ * @param endDir the direction enum
+ * @param numSublines number of sublines (useful to represent vec3 or rgb)
+ **/
+ renderLink(
+ a: Vector2,
+ b: Vector2,
+ link: object,
+ skipBorder: boolean,
+ flow: boolean,
+ color?: string,
+ startDir?: number,
+ endDir?: number,
+ numSublines?: number
+ ): void;
+
+ computeConnectionPoint(
+ a: Vector2,
+ b: Vector2,
+ t: number,
+ startDir?: number,
+ endDir?: number
+ ): void;
+
+ drawExecutionOrder(ctx: CanvasRenderingContext2D): void;
+ /** draws the widgets stored inside a node */
+ drawNodeWidgets(
+ node: LGraphNode,
+ posY: number,
+ ctx: CanvasRenderingContext2D,
+ activeWidget: object
+ ): void;
+ /** process an event on widgets */
+ processNodeWidgets(
+ node: LGraphNode,
+ pos: Vector2,
+ event: Event,
+ activeWidget: object
+ ): void;
+ /** draws every group area in the background */
+ drawGroups(canvas: any, ctx: CanvasRenderingContext2D): void;
+ adjustNodesSize(): void;
+ /** resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode */
+ resize(width?: number, height?: number): void;
+ /**
+ * switches to live mode (node shapes are not rendered, only the content)
+ * this feature was designed when graphs where meant to create user interfaces
+ **/
+ switchLiveMode(transition?: boolean): void;
+ onNodeSelectionChange(): void;
+ touchHandler(event: TouchEvent): void;
+
+ showLinkMenu(link: LLink, e: any): false;
+ prompt(
+ title: string,
+ value: any,
+ callback: Function,
+ event: any
+ ): HTMLDivElement;
+ showSearchBox(event?: MouseEvent): void;
+ showEditPropertyValue(node: LGraphNode, property: any, options: any): void;
+ createDialog(
+ html: string,
+ options?: { position?: Vector2; event?: MouseEvent }
+ ): void;
+
+ convertOffsetToCanvas: DragAndScale["convertOffsetToCanvas"];
+ convertCanvasToOffset: DragAndScale["convertCanvasToOffset"];
+ /** converts event coordinates from canvas2D to graph coordinates */
+ convertEventToCanvasOffset(e: MouseEvent): Vector2;
+ /** adds some useful properties to a mouse event, like the position in graph coordinates */
+ adjustMouseEvent(e: MouseEvent): void;
+
+ getCanvasMenuOptions(): ContextMenuItem[];
+ getNodeMenuOptions(node: LGraphNode): ContextMenuItem[];
+ getGroupMenuOptions(): ContextMenuItem[];
+ /** Called by `getCanvasMenuOptions`, replace default options */
+ getMenuOptions?(): ContextMenuItem[];
+ /** Called by `getCanvasMenuOptions`, append to default options */
+ getExtraMenuOptions?(): ContextMenuItem[];
+ /** Called when mouse right click */
+ processContextMenu(node: LGraphNode, event: Event): void;
+ }
+
+ declare class ContextMenu {
+ static trigger(
+ element: HTMLElement,
+ event_name: string,
+ params: any,
+ origin: any
+ ): void;
+ static isCursorOverElement(event: MouseEvent, element: HTMLElement): void;
+ static closeAllContextMenus(window: Window): void;
+ constructor(values: ContextMenuItem[], options?: IContextMenuOptions, window?: Window);
+ options: IContextMenuOptions;
+ parentMenu?: ContextMenu;
+ lock: boolean;
+ current_submenu?: ContextMenu;
+ addItem(
+ name: string,
+ value: ContextMenuItem,
+ options?: IContextMenuOptions
+ ): void;
+ close(e?: MouseEvent, ignore_parent_menu?: boolean): void;
+ getTopMenu(): void;
+ getFirstEvent(): void;
+ }
+
+ declare global {
+ interface CanvasRenderingContext2D {
+ /** like rect but rounded corners */
+ roundRect(
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ radius: number,
+ radiusLow: number
+ ): void;
+ }
+
+ interface Math {
+ clamp(v: number, min: number, max: number): number;
+ }
+ }
+}
diff --git a/static/favicon.png b/static/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..825b9e65af7c104cfb07089bb28659393b4f2097
GIT binary patch
literal 1571
zcmV+;2Hg3HP)Px)-AP12RCwC$UE6KzI1p6{F2N
z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z
zgj#$x=!~7LGqHW?IO8+*oE1MyDp!G=L0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH
z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;|
zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f
z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb
zghxQU22N}F51}^yfDSt786oMTc!W&V;d?76)9KXX1
z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFNgpIod~R{>@#@5x9zJKEHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe
ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2`
zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F
z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL
z@iUO?SBBG-0cQuo+an4TsLy-g-x;8P4UVwk|D8{W@U1Zi
z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y
zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA
zI8U?CWdY@}cRq6{5~y+)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6
zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6
zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5
zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH