commit 546b980c8dbc2c34b679bfa197cffef1e87b0ebc
Author: space-nuko <24979496+space-nuko@users.noreply.github.com>
Date: Tue Apr 4 15:10:09 2023 -0500
First commit
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 0000000..825b9e6
Binary files /dev/null and b/static/favicon.png differ
diff --git a/svelte.config.js b/svelte.config.js
new file mode 100644
index 0000000..b6f23ec
--- /dev/null
+++ b/svelte.config.js
@@ -0,0 +1,22 @@
+import adapter from '@sveltejs/adapter-auto';
+import sveltePreprocess from "svelte-preprocess";
+
+const config = {
+ kit: {
+ // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
+ // If your environment is not supported or you settled on a specific environment, switch out the adapter.
+ // See https://kit.svelte.dev/docs/adapters for more information about adapters.
+ adapter: adapter()
+ },
+ preprocess: [
+ sveltePreprocess({
+ typescript: {
+ compilerOptions: {
+ debug: true,
+ }
+ }
+ })
+ ]
+};
+
+export default config;
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..aa43546
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "exclude": ["node_modules/litegraph.js/src/*"],
+ "compilerOptions": {
+ "baseUrl": "./src",
+ "typeRoots": [
+ "types",
+ "../node_modules/@types"
+ ],
+ "paths": {
+ "$lib": ["../src/lib"],
+ "$lib/*": ["../src/lib/*"]
+ }
+ }
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..14a0beb
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,16 @@
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vitest/config';
+import FullReload from 'vite-plugin-full-reload'
+
+export default defineConfig({
+ plugins: [
+ sveltekit(),
+ FullReload(["src/**/*.{js,ts,svelte}"])
+ ],
+ server: {
+ port: 3000
+ },
+ test: {
+ include: ['src/**/*.{test,spec}.{js,ts}']
+ }
+});