Journey functionality

This commit is contained in:
space-nuko
2023-06-02 22:28:18 -05:00
parent 4f237d01a5
commit ab6266704c
11 changed files with 537 additions and 56 deletions

View File

@@ -20,6 +20,8 @@
"devDependencies": { "devDependencies": {
"@floating-ui/core": "^1.2.6", "@floating-ui/core": "^1.2.6",
"@floating-ui/dom": "^1.2.8", "@floating-ui/dom": "^1.2.8",
"@types/cytoscape": "^3.19.9",
"@types/dompurify": "^3.0.2",
"@zerodevx/svelte-toast": "^0.9.3", "@zerodevx/svelte-toast": "^0.9.3",
"eslint": "^8.37.0", "eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
@@ -34,8 +36,6 @@
"svelte-check": "^3.2.0", "svelte-check": "^3.2.0",
"svelte-dnd-action": "^0.9.22", "svelte-dnd-action": "^0.9.22",
"typescript": "^5.0.3", "typescript": "^5.0.3",
"@types/cytoscape": "^3.19.9",
"@types/dompurify": "^3.0.2",
"vite": "^4.3.8", "vite": "^4.3.8",
"vite-plugin-glsl": "^1.1.2", "vite-plugin-glsl": "^1.1.2",
"vite-plugin-static-copy": "^0.14.0", "vite-plugin-static-copy": "^0.14.0",
@@ -84,6 +84,7 @@
"csv-parse": "^5.3.10", "csv-parse": "^5.3.10",
"cytoscape": "^3.25.0", "cytoscape": "^3.25.0",
"cytoscape-dagre": "^2.5.0", "cytoscape-dagre": "^2.5.0",
"deep-equal": "^2.2.1",
"dompurify": "^3.0.3", "dompurify": "^3.0.3",
"events": "^3.3.0", "events": "^3.3.0",
"framework7": "^8.0.3", "framework7": "^8.0.3",

286
pnpm-lock.yaml generated
View File

@@ -121,6 +121,9 @@ importers:
cytoscape-dagre: cytoscape-dagre:
specifier: ^2.5.0 specifier: ^2.5.0
version: 2.5.0(cytoscape@3.25.0) version: 2.5.0(cytoscape@3.25.0)
deep-equal:
specifier: ^2.2.1
version: 2.2.1
dompurify: dompurify:
specifier: ^3.0.3 specifier: ^3.0.3
version: 3.0.3 version: 3.0.3
@@ -1372,7 +1375,6 @@ packages:
'@codemirror/language': ^6.0.0 '@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0 '@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0 '@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
dependencies: dependencies:
'@codemirror/language': 6.6.0 '@codemirror/language': 6.6.0
'@codemirror/state': 6.2.0 '@codemirror/state': 6.2.0
@@ -2715,6 +2717,13 @@ packages:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: true dev: true
/array-buffer-byte-length@1.0.0:
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
dependencies:
call-bind: 1.0.2
is-array-buffer: 3.0.2
dev: false
/array-union@2.1.0: /array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -2765,6 +2774,11 @@ packages:
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
dev: true dev: true
/available-typed-arrays@1.0.5:
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
engines: {node: '>= 0.4'}
dev: false
/aws-sign2@0.7.0: /aws-sign2@0.7.0:
resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
dev: false dev: false
@@ -3131,8 +3145,6 @@ packages:
'@codemirror/search': 6.4.0 '@codemirror/search': 6.4.0
'@codemirror/state': 6.2.0 '@codemirror/state': 6.2.0
'@codemirror/view': 6.11.0 '@codemirror/view': 6.11.0
transitivePeerDependencies:
- '@lezer/common'
dev: false dev: false
/codemirror@6.0.1(@lezer/common@1.0.2): /codemirror@6.0.1(@lezer/common@1.0.2):
@@ -3576,6 +3588,29 @@ packages:
dependencies: dependencies:
type-detect: 4.0.8 type-detect: 4.0.8
/deep-equal@2.2.1:
resolution: {integrity: sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==}
dependencies:
array-buffer-byte-length: 1.0.0
call-bind: 1.0.2
es-get-iterator: 1.1.3
get-intrinsic: 1.2.0
is-arguments: 1.1.1
is-array-buffer: 3.0.2
is-date-object: 1.0.5
is-regex: 1.1.4
is-shared-array-buffer: 1.0.2
isarray: 2.0.5
object-is: 1.1.5
object-keys: 1.1.1
object.assign: 4.1.4
regexp.prototype.flags: 1.5.0
side-channel: 1.0.4
which-boxed-primitive: 1.0.2
which-collection: 1.0.1
which-typed-array: 1.1.9
dev: false
/deep-is@0.1.4: /deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true dev: true
@@ -3589,6 +3624,14 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/define-properties@1.2.0:
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
engines: {node: '>= 0.4'}
dependencies:
has-property-descriptors: 1.0.0
object-keys: 1.1.1
dev: false
/delaunator@5.0.0: /delaunator@5.0.0:
resolution: {integrity: sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==} resolution: {integrity: sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==}
dependencies: dependencies:
@@ -3690,6 +3733,20 @@ packages:
is-arrayish: 0.2.1 is-arrayish: 0.2.1
dev: true dev: true
/es-get-iterator@1.1.3:
resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.0
has-symbols: 1.0.3
is-arguments: 1.1.1
is-map: 2.0.2
is-set: 2.0.2
is-string: 1.0.7
isarray: 2.0.5
stop-iteration-iterator: 1.0.0
dev: false
/es6-promise@3.3.1: /es6-promise@3.3.1:
resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
@@ -4295,6 +4352,12 @@ packages:
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
dev: true dev: true
/for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies:
is-callable: 1.2.7
dev: false
/forever-agent@0.6.1: /forever-agent@0.6.1:
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
dev: false dev: false
@@ -4385,6 +4448,10 @@ packages:
/function-bind@1.1.1: /function-bind@1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
/functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: false
/gensync@1.0.0-beta.2: /gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@@ -4495,6 +4562,12 @@ packages:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
dev: true dev: true
/gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
get-intrinsic: 1.2.0
dev: false
/graceful-fs@4.2.11: /graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -4533,6 +4606,10 @@ packages:
har-schema: 2.0.0 har-schema: 2.0.0
dev: false dev: false
/has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
dev: false
/has-flag@3.0.0: /has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -4543,11 +4620,24 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/has-property-descriptors@1.0.0:
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
dependencies:
get-intrinsic: 1.2.0
dev: false
/has-symbols@1.0.3: /has-symbols@1.0.3:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: false dev: false
/has-tostringtag@1.0.0:
resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
engines: {node: '>= 0.4'}
dependencies:
has-symbols: 1.0.3
dev: false
/has@1.0.3: /has@1.0.3:
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
engines: {node: '>= 0.4.0'} engines: {node: '>= 0.4.0'}
@@ -4686,26 +4776,77 @@ packages:
/inherits@2.0.4: /inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
/internal-slot@1.0.5:
resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.0
has: 1.0.3
side-channel: 1.0.4
dev: false
/internmap@2.0.3: /internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'} engines: {node: '>=12'}
dev: false dev: false
/is-arguments@1.1.1:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
has-tostringtag: 1.0.0
dev: false
/is-array-buffer@3.0.2:
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.0
is-typed-array: 1.1.10
dev: false
/is-arrayish@0.2.1: /is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: true dev: true
/is-bigint@1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
has-bigints: 1.0.2
dev: false
/is-binary-path@2.1.0: /is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
binary-extensions: 2.2.0 binary-extensions: 2.2.0
/is-boolean-object@1.1.2:
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
has-tostringtag: 1.0.0
dev: false
/is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
dev: false
/is-core-module@2.12.0: /is-core-module@2.12.0:
resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==}
dependencies: dependencies:
has: 1.0.3 has: 1.0.3
/is-date-object@1.0.5:
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.0
dev: false
/is-docker@2.2.1: /is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -4732,6 +4873,17 @@ packages:
dependencies: dependencies:
is-extglob: 2.1.1 is-extglob: 2.1.1
/is-map@2.0.2:
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
dev: false
/is-number-object@1.0.7:
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.0
dev: false
/is-number@7.0.0: /is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'} engines: {node: '>=0.12.0'}
@@ -4745,15 +4897,69 @@ packages:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
dev: true dev: true
/is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
has-tostringtag: 1.0.0
dev: false
/is-set@2.0.2:
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
dev: false
/is-shared-array-buffer@1.0.2:
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
dependencies:
call-bind: 1.0.2
dev: false
/is-stream@2.0.1: /is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/is-string@1.0.7:
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
engines: {node: '>= 0.4'}
dependencies:
has-tostringtag: 1.0.0
dev: false
/is-symbol@1.0.4:
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
engines: {node: '>= 0.4'}
dependencies:
has-symbols: 1.0.3
dev: false
/is-typed-array@1.1.10:
resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==}
engines: {node: '>= 0.4'}
dependencies:
available-typed-arrays: 1.0.5
call-bind: 1.0.2
for-each: 0.3.3
gopd: 1.0.1
has-tostringtag: 1.0.0
dev: false
/is-typedarray@1.0.0: /is-typedarray@1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
dev: false dev: false
/is-weakmap@2.0.1:
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
dev: false
/is-weakset@2.0.2:
resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.0
dev: false
/is-wsl@2.2.0: /is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -4765,6 +4971,10 @@ packages:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: false dev: false
/isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
dev: false
/isexe@2.0.0: /isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true dev: true
@@ -5763,6 +5973,29 @@ packages:
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
dev: false dev: false
/object-is@1.1.5:
resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
dev: false
/object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
dev: false
/object.assign@4.1.4:
resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
has-symbols: 1.0.3
object-keys: 1.1.1
dev: false
/once@1.4.0: /once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies: dependencies:
@@ -6182,6 +6415,15 @@ packages:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
dev: false dev: false
/regexp.prototype.flags@1.5.0:
resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
functions-have-names: 1.2.3
dev: false
/request@2.88.2: /request@2.88.2:
resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@@ -6554,6 +6796,13 @@ packages:
/std-env@3.3.3: /std-env@3.3.3:
resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==}
/stop-iteration-iterator@1.0.0:
resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
engines: {node: '>= 0.4'}
dependencies:
internal-slot: 1.0.5
dev: false
/stream-transform@3.2.6: /stream-transform@3.2.6:
resolution: {integrity: sha512-/pyOvaCQFqYTmrFhmMbnAEVo3SsTx1H39eUVPOtYeAgbEUc+rDo7GoP8LbHJgU83mKtzJe/7Nq/ipaAnUOHgJQ==} resolution: {integrity: sha512-/pyOvaCQFqYTmrFhmMbnAEVo3SsTx1H39eUVPOtYeAgbEUc+rDo7GoP8LbHJgU83mKtzJe/7Nq/ipaAnUOHgJQ==}
dev: false dev: false
@@ -8408,6 +8657,37 @@ packages:
webidl-conversions: 3.0.1 webidl-conversions: 3.0.1
dev: false dev: false
/which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies:
is-bigint: 1.0.4
is-boolean-object: 1.1.2
is-number-object: 1.0.7
is-string: 1.0.7
is-symbol: 1.0.4
dev: false
/which-collection@1.0.1:
resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
dependencies:
is-map: 2.0.2
is-set: 2.0.2
is-weakmap: 2.0.1
is-weakset: 2.0.2
dev: false
/which-typed-array@1.1.9:
resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==}
engines: {node: '>= 0.4'}
dependencies:
available-typed-arrays: 1.0.5
call-bind: 1.0.2
for-each: 0.3.3
gopd: 1.0.1
has-tostringtag: 1.0.0
is-typed-array: 1.1.10
dev: false
/which@2.0.2: /which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}

View File

@@ -7,11 +7,12 @@
import type ComfyApp from './ComfyApp'; import type ComfyApp from './ComfyApp';
import type { ComfyBoxWorkflow } from '$lib/stores/workflowState'; import type { ComfyBoxWorkflow } from '$lib/stores/workflowState';
import workflowState from '$lib/stores/workflowState'; import workflowState from '$lib/stores/workflowState';
import type { WritableJourneyStateStore } from '$lib/stores/journeyStates'; import { calculateWorkflowParamsPatch, resolvePatch, type JourneyPatchNode, type WritableJourneyStateStore } from '$lib/stores/journeyStates';
import JourneyRenderer from './JourneyRenderer.svelte'; import JourneyRenderer from './JourneyRenderer.svelte';
import { Plus } from "svelte-bootstrap-icons"; import { Plus } from "svelte-bootstrap-icons";
import { getWorkflowRestoreParams, getWorkflowRestoreParamsFromWorkflow } from '$lib/restoreParameters'; import { getWorkflowRestoreParams, getWorkflowRestoreParamsFromWorkflow } from '$lib/restoreParameters';
import notify from '$lib/notify'; import notify from '$lib/notify';
import selectionState from '$lib/stores/selectionState';
export let app: ComfyApp; export let app: ComfyApp;
@@ -27,20 +28,86 @@
return; return;
} }
const nodes = Array.from(journey.iterateBreadthFirst());
let parent = null;
if (nodes.length > 0)
parent = nodes[nodes.length - 1]
const workflowParams = getWorkflowRestoreParamsFromWorkflow(workflow) const workflowParams = getWorkflowRestoreParamsFromWorkflow(workflow)
journey.addNode(workflowParams, parent?.id); const activeNode = journey.getActiveNode();
let journeyNode
if (activeNode == null) {
// add root node
if ($journey.root != null) {
return;
}
journeyNode = journey.addNode(workflowParams, null);
notify("Pushed a new base workflow state.", { type: "info" })
}
else {
// add patch node
const patch = calculateWorkflowParamsPatch(activeNode, workflowParams);
const patchedCount = Object.keys(patch).length;
if (patchedCount === 0) {
notify("No changes were made to active parameters yet.", { type: "warning" })
return;
}
journeyNode = journey.addNode(patch, activeNode);
notify(`Pushed new state with ${patchedCount} changes.`, { type: "info" })
}
if (journeyNode != null) {
journey.selectNode(journeyNode);
}
}
function onSelectNode(e: CustomEvent<{ cyto: cytoscape.Core, node: cytoscape.NodeSingular }>) {
const { node } = e.detail;
const id = node.id();
const journeyNode = $journey.nodesByID[id];
if (journeyNode == null) {
console.error("[ComfyJourneyView] Missing journey node!", id)
return;
}
const patch = resolvePatch(journeyNode);
// ensure reactive state is updated
workflow.applyParamsPatch(patch);
$workflowState = $workflowState
}
function onHoverNode(e: CustomEvent<{ cyto: cytoscape.Core, node: cytoscape.NodeSingular }>) {
const { node } = e.detail;
const id = node.id();
const journeyNode = $journey.nodesByID[id];
if (journeyNode == null) {
console.error("[ComfyJourneyView] Missing journey node!", id)
return;
}
if (journeyNode.type === "patch") {
$selectionState.currentPatchHoveredNodes = new Set(Object.keys((journeyNode as JourneyPatchNode).patch))
}
else {
$selectionState.currentPatchHoveredNodes = new Set();
}
}
function onHoverNodeOut(e: CustomEvent<{ cyto: cytoscape.Core, node: cytoscape.NodeSingular }>) {
$selectionState.currentPatchHoveredNodes = new Set();
} }
</script> </script>
<div class="journey-view"> <div class="journey-view">
<JourneyRenderer {workflow} {journey} /> <JourneyRenderer {workflow} {journey}
on:select_node={onSelectNode}
on:hover_node={onHoverNode}
on:hover_node_out={onHoverNodeOut}
/>
<div class="bottom"> <div class="bottom">
<button class="mode-button ternary" <button class="mode-button ternary"
title={"Add new"} title={"Add new"}
disabled={$journey.activeNodeID === null && $journey.root !== null}
on:click={doAdd}> on:click={doAdd}>
<Plus width="100%" height="100%" /> <Plus width="100%" height="100%" />
</button> </button>

View File

@@ -4,34 +4,19 @@
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import Graph from './graph/Graph.svelte' import Graph from './graph/Graph.svelte'
import type { NodeDataDefinition, EdgeDataDefinition } from 'cytoscape'; import type { NodeDataDefinition, EdgeDataDefinition } from 'cytoscape';
import { createEventDispatcher } from "svelte";
import selectionState from '$lib/stores/selectionState';
export let workflow: ComfyBoxWorkflow | null = null export let workflow: ComfyBoxWorkflow | null = null
export let journey: WritableJourneyStateStore | null = null export let journey: WritableJourneyStateStore | null = null
//
// const nodes: NodeDataDefinition[] = [
// //{ id: 'N1', label: 'Start' },
// //{ id: 'N2', label: '4' },
// //{ id: 'N4', label: '8' },
// //{ id: 'N5', label: '15' },
// //{ id: 'N3', label: '16' },
// //{ id: 'N6', label: '23' },
// //{ id: 'N7', label: '42' },
// //{ id: 'N8', label: 'End' }
// ]
//
// const edges: EdgeDataDefinition[] = [
// //{ id: 'E1', source: 'N1', target: 'N2' },
// //{ id: 'E2', source: 'N2', target: 'N3' },
// //{ id: 'E3', source: 'N3', target: 'N6' },
// //{ id: 'E4', source: 'N2', target: 'N4' },
// //{ id: 'E5', source: 'N4', target: 'N5' },
// //{ id: 'E6', source: 'N5', target: 'N4', label: '2' },
// //{ id: 'E7', source: 'N5', target: 'N6' },
// //{ id: 'E8', source: 'N6', target: 'N7' },
// //{ id: 'E9', source: 'N7', target: 'N7', label: '3' },
// //{ id: 'E10', source: 'N7', target: 'N8' }
// ]
const dispatch = createEventDispatcher<{
select_node: { cyto: cytoscape.Core, node: cytoscape.NodeSingular };
hover_node: { cyto: cytoscape.Core, node: cytoscape.NodeSingular };
hover_node_out: { cyto: cytoscape.Core, node: cytoscape.NodeSingular };
}>();
let lastSelected = null;
let lastVersion = -1; let lastVersion = -1;
@@ -48,6 +33,7 @@
} }
const journeyState = get(journey); const journeyState = get(journey);
lastSelected = journeyState.activeNodeID;
const nodes: NodeDataDefinition[] = [] const nodes: NodeDataDefinition[] = []
const edges: EdgeDataDefinition[] = [] const edges: EdgeDataDefinition[] = []
@@ -86,17 +72,48 @@
function onNodeSelected(e: cytoscape.InputEventObject) { function onNodeSelected(e: cytoscape.InputEventObject) {
console.warn("SELECT", e) console.warn("SELECT", e)
const node = e.target; const node = e.target as cytoscape.NodeSingular;
journey.selectNode(node.id()); journey.selectNode(node.id());
e.cy.animate({
center: { eles: node }
}, {
duration: 400,
easing: "ease-in-out-quad"
});
e.cy.center(node) e.cy.center(node)
dispatch("select_node", { cyto: e.cy, node })
}
function onNodeHovered(e: cytoscape.InputEventObject) {
const node = e.target as cytoscape.NodeSingular;
dispatch("hover_node", { cyto: e.cy, node })
}
function onNodeHoveredOut(e: cytoscape.InputEventObject) {
const node = e.target as cytoscape.NodeSingular;
dispatch("hover_node_out", { cyto: e.cy, node })
} }
function onRebuilt(e: CustomEvent<{cyto: cytoscape.Core}>) { function onRebuilt(e: CustomEvent<{cyto: cytoscape.Core}>) {
const { cyto } = e.detail; const { cyto } = e.detail;
for (const node of cyto.nodes().components()) {
if (node.id() === lastSelected) {
// why doesn't passing `selected` work in the ctor?
node.select();
}
}
$selectionState.currentPatchHoveredNodes = new Set()
cyto.nodes() cyto.nodes()
.lock() .lock()
.on("select", onNodeSelected) .on("select", onNodeSelected)
.on("mouseover", onNodeHovered)
.on("mouseout", onNodeHoveredOut)
const nodes = Array.from(journey.iterateBreadthFirst()); const nodes = Array.from(journey.iterateBreadthFirst());
if (nodes.length > 0) { if (nodes.length > 0) {

View File

@@ -84,6 +84,7 @@
class:edit={edit} class:edit={edit}
class:hovered class:hovered
class:selected class:selected
class:patch-affected={$selectionState.currentPatchHoveredNodes.has(widget.node.id)}
class:is-executing={$queueState.runningNodeID && $queueState.runningNodeID == widget.node.id} class:is-executing={$queueState.runningNodeID && $queueState.runningNodeID == widget.node.id}
class:hidden={hidden} class:hidden={hidden}
> >
@@ -112,6 +113,10 @@
&.selected { &.selected {
background: var(--comfy-widget-selected-background-fill); background: var(--comfy-widget-selected-background-fill);
} }
&.patch-affected {
background: var(--secondary-500);
}
} }
.is-executing { .is-executing {

View File

@@ -30,9 +30,6 @@
cyInstance = null; cyInstance = null;
} }
let _nodes: any[];
let _edges: any[];
function rebuildGraph() { function rebuildGraph() {
cytoscape.use(dagre) cytoscape.use(dagre)
@@ -41,6 +38,7 @@
style: GraphStyles, style: GraphStyles,
wheelSensitivity: 0.1, wheelSensitivity: 0.1,
maxZoom: 1, maxZoom: 1,
minZoom: 0.5,
}) })
cyInstance.on("add", () => { cyInstance.on("add", () => {
@@ -74,8 +72,7 @@
let cyInstance: cytoscape.Core = null let cyInstance: cytoscape.Core = null
</script> </script>
<div class="cy-graph" {style} bind:this={refElement}> <div class="cy-graph" {style} bind:this={refElement} />
</div>
<style lang="scss"> <style lang="scss">
.cy-graph { .cy-graph {

View File

@@ -1,10 +1,11 @@
import { get, writable } from 'svelte/store'; import { get, writable } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store'; import type { Readable, Writable } from 'svelte/store';
import type { DragItemID, IDragItem } from './layoutStates'; import type { DragItemID, IDragItem } from './layoutStates';
import type { LGraphNode, NodeID, UUID } from '@litegraph-ts/core'; import { LiteGraph, type LGraphNode, type NodeID, type UUID } from '@litegraph-ts/core';
import type { SerializedAppState } from '$lib/components/ComfyApp'; import type { SerializedAppState } from '$lib/components/ComfyApp';
import type { RestoreParamTargets, RestoreParamWorkflowNodeTargets } from '$lib/restoreParameters'; import type { RestoreParamTargets, RestoreParamWorkflowNodeTargets } from '$lib/restoreParameters';
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import deepEqual from "deep-equal";
export type JourneyNodeType = "root" | "patch"; export type JourneyNodeType = "root" | "patch";
@@ -55,7 +56,7 @@ function diffParams(base: RestoreParamWorkflowNodeTargets, updated: RestoreParam
const result = {} const result = {}
for (const [k, v] of Object.entries(updated)) { for (const [k, v] of Object.entries(updated)) {
if (!(k in base) || base[k].finalValue !== v) { if (!(k in base) || !deepEqual(base[k].finalValue, v.finalValue, { strict: true })) {
result[k] = v result[k] = v
} }
} }
@@ -63,7 +64,7 @@ function diffParams(base: RestoreParamWorkflowNodeTargets, updated: RestoreParam
return result; return result;
} }
function calculatePatch(parent: JourneyNode, newParams: RestoreParamWorkflowNodeTargets): RestoreParamWorkflowNodeTargets { export function calculateWorkflowParamsPatch(parent: JourneyNode, newParams: RestoreParamWorkflowNodeTargets): RestoreParamWorkflowNodeTargets {
const patch = resolvePatch(parent); const patch = resolvePatch(parent);
const diff = diffParams(patch, newParams) const diff = diffParams(patch, newParams)
return diff; return diff;
@@ -87,8 +88,9 @@ export type JourneyState = {
type JourneyStateOps = { type JourneyStateOps = {
clear: () => void, clear: () => void,
addNode: (params: RestoreParamWorkflowNodeTargets, parent?: JourneyNodeID) => JourneyNode, getActiveNode: () => JourneyNode | null,
selectNode: (id?: JourneyNodeID) => void, addNode: (params: RestoreParamWorkflowNodeTargets, parent?: JourneyNodeID | JourneyNode) => JourneyNode,
selectNode: (id?: JourneyNodeID | JourneyNode) => void,
iterateBreadthFirst: (id?: JourneyNodeID | null) => Iterable<JourneyNode> iterateBreadthFirst: (id?: JourneyNodeID | null) => Iterable<JourneyNode>
} }
@@ -112,15 +114,28 @@ function create() {
}) })
} }
function getActiveNode(): JourneyNode | null {
const state = get(store)
if (state.activeNodeID === null)
return null;
const active = state.nodesByID[state.activeNodeID]
if (active == null) {
console.error("[journeyStates] Active node not found in graph!", state.activeNodeID);
}
return active;
}
/* /*
* params: full state of widgets in the UI * params: full state or state patch of widgets in the UI
* parent: parent node to patch against * parent: parent node to patch against
*/ */
function addNode(params: RestoreParamWorkflowNodeTargets, parent?: JourneyNodeID): JourneyNode { function addNode(params: RestoreParamWorkflowNodeTargets, parent?: JourneyNodeID | JourneyNode): JourneyNode {
let _node: JourneyRootNode | JourneyPatchNode; let _node: JourneyRootNode | JourneyPatchNode;
store.update(s => { store.update(s => {
let parentNode: JourneyNode | null = null let parentNode: JourneyNode | null = null
if (parent != null) { if (parent != null) {
if (typeof parent === "object")
parent = parent.id;
parentNode = s.nodesByID[parent]; parentNode = s.nodesByID[parent];
if (parentNode == null) { if (parentNode == null) {
throw new Error(`Could not find parent node ${parent} to insert into!`) throw new Error(`Could not find parent node ${parent} to insert into!`)
@@ -141,7 +156,7 @@ function create() {
type: "patch", type: "patch",
parent: parentNode, parent: parentNode,
children: [], children: [],
patch: calculatePatch(parentNode, params) patch: params,
} }
parentNode.children.push(_node); parentNode.children.push(_node);
} }
@@ -152,9 +167,12 @@ function create() {
return _node; return _node;
} }
function selectNode(id?: JourneyNodeID) { function selectNode(obj?: JourneyNodeID | JourneyNode) {
store.update(s => { store.update(s => {
s.activeNodeID = id; if (typeof obj === "string")
s.activeNodeID = obj;
else
s.activeNodeID = obj.id;
return s; return s;
}) })
} }
@@ -201,6 +219,7 @@ function create() {
return { return {
...store, ...store,
getActiveNode,
clear, clear,
addNode, addNode,
selectNode, selectNode,

View File

@@ -24,7 +24,12 @@ export type SelectionState = {
/* /*
* Currently hovered nodes. * Currently hovered nodes.
*/ */
currentHoveredNodes: Set<NodeID> currentHoveredNodes: Set<NodeID>,
/*
* Nodes affected by the patch hovered in the journey pane
*/
currentPatchHoveredNodes: Set<NodeID>
} }
type SelectionStateOps = { type SelectionStateOps = {
@@ -38,6 +43,7 @@ const store: Writable<SelectionState> = writable(
currentSelectionNodes: [], currentSelectionNodes: [],
currentHovered: new Set(), currentHovered: new Set(),
currentHoveredNodes: new Set(), currentHoveredNodes: new Set(),
currentPatchHoveredNodes: new Set(),
}) })
function clear() { function clear() {
@@ -46,12 +52,13 @@ function clear() {
currentSelectionNodes: [], currentSelectionNodes: [],
currentHovered: new Set(), currentHovered: new Set(),
currentHoveredNodes: new Set(), currentHoveredNodes: new Set(),
currentPatchHoveredNodes: new Set(),
}) })
} }
const uiStateStore: WritableSelectionStateStore = const selectionStateStore: WritableSelectionStateStore =
{ {
...store, ...store,
clear clear
} }
export default uiStateStore; export default selectionStateStore;

View File

@@ -2,7 +2,7 @@ import type { SerializedGraphCanvasState } from '$lib/ComfyGraphCanvas';
import { clamp, LGraphNode, type LGraphCanvas, type NodeID, type SerializedLGraph, type UUID, LGraph, LiteGraph, type SlotType, NodeMode } from '@litegraph-ts/core'; import { clamp, LGraphNode, type LGraphCanvas, type NodeID, type SerializedLGraph, type UUID, LGraph, LiteGraph, type SlotType, NodeMode } from '@litegraph-ts/core';
import { get, writable } from 'svelte/store'; import { get, writable } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store'; import type { Readable, Writable } from 'svelte/store';
import { defaultWorkflowAttributes, type SerializedLayoutState, type WritableLayoutStateStore } from './layoutStates'; import { defaultWorkflowAttributes, isComfyWidgetNode, type SerializedLayoutState, type WritableLayoutStateStore } from './layoutStates';
import ComfyGraph from '$lib/ComfyGraph'; import ComfyGraph from '$lib/ComfyGraph';
import layoutStates from './layoutStates'; import layoutStates from './layoutStates';
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
@@ -14,6 +14,7 @@ import type { ComfyBoxPromptExtraData, PromptID } from '$lib/api';
import type { ComfyAPIPromptErrorResponse, ComfyExecutionError } from '$lib/apiErrors'; import type { ComfyAPIPromptErrorResponse, ComfyExecutionError } from '$lib/apiErrors';
import type { WritableJourneyStateStore } from './journeyState'; import type { WritableJourneyStateStore } from './journeyState';
import journeyStates from './journeyStates'; import journeyStates from './journeyStates';
import type { RestoreParamWorkflowNodeTargets } from '$lib/restoreParameters';
type ActiveCanvas = { type ActiveCanvas = {
canvas: LGraphCanvas | null; canvas: LGraphCanvas | null;
@@ -212,6 +213,21 @@ export class ComfyBoxWorkflow {
} }
} }
applyParamsPatch(patch: RestoreParamWorkflowNodeTargets) {
for (const [nodeId, source] of Object.entries(patch)) {
const node = this.graph.getNodeByIdRecursive(nodeId);
if (node == null) {
console.error("[applyParamsPatch] Node was missing in patch!!", nodeId, source)
continue;
}
if (!isComfyWidgetNode(node)) {
console.error("[applyParamsPatch] Node was not ComfyWidgetNode!!", nodeId, source)
continue;
}
node.setValue(source.finalValue);
}
}
/* /*
* Creates a workflow and layout. * Creates a workflow and layout.
* *

View File

@@ -0,0 +1,71 @@
import { get } from "svelte/store";
import journeyState, { type JourneyState } from "$lib/stores/journeyState"
import { expect } from 'vitest';
import UnitTest from "../UnitTest";
import { Watch } from "@litegraph-ts/nodes-basic";
import { ComfyBoxWorkflow } from "$lib/stores/workflowState";
import { ComfyNumberNode } from "$lib/nodes/widgets";
import { LiteGraph } from "@litegraph-ts/core";
import { getWorkflowRestoreParamsFromWorkflow } from "$lib/restoreParameters";
import { calculateWorkflowParamsPatch } from "$lib/stores/journeyStates";
export default class journeyStateTests extends UnitTest {
test__patches() {
const [workflow, layoutState] = ComfyBoxWorkflow.create()
const { graph, journey } = workflow;
layoutState.initDefaultLayout() // adds 3 containers
const widget1 = LiteGraph.createNode(ComfyNumberNode);
const widget2 = LiteGraph.createNode(ComfyNumberNode);
const watch1 = LiteGraph.createNode(Watch);
const watch2 = LiteGraph.createNode(Watch);
graph.add(widget1)
graph.add(watch1)
graph.add(widget2)
graph.add(watch2)
widget1.connect(0, watch1, 0);
widget2.connect(0, watch2, 0);
widget1.setValue(0)
widget2.setValue(0)
let workflowParams = getWorkflowRestoreParamsFromWorkflow(workflow)
const root = journey.addNode(workflowParams, null);
expect(root).toEqual({
id: root.id,
type: "root",
children: [],
base: {
[widget1.id]: {
type: "workflow",
finalValue: 0,
},
[widget2.id]: {
type: "workflow",
finalValue: 0,
}
}
});
widget1.setValue(5)
workflowParams = getWorkflowRestoreParamsFromWorkflow(workflow)
const patchParams = calculateWorkflowParamsPatch(root, workflowParams)
const patch = journey.addNode(patchParams, root.id);
expect(patch).toEqual({
id: patch.id,
type: "patch",
parent: root,
children: [],
patch: {
[widget1.id]: {
type: "workflow",
finalValue: 5
},
}
})
}
}

View File

@@ -4,3 +4,4 @@ export { default as parseA1111Tests } from "./parseA1111Tests"
export { default as convertA1111ToStdPromptTests } from "./convertA1111ToStdPromptTests" export { default as convertA1111ToStdPromptTests } from "./convertA1111ToStdPromptTests"
export { default as convertVanillaWorkflowTest } from "./convertVanillaWorkflowTests" export { default as convertVanillaWorkflowTest } from "./convertVanillaWorkflowTests"
export { default as configStateTests } from "./stores/configStateTests" export { default as configStateTests } from "./stores/configStateTests"
export { default as journeyStates } from "./stores/journeyStatesTests"