attempt at nodes
This commit is contained in:
parent
73f3c57690
commit
473b46edf2
|
@ -14,6 +14,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||
"@xyflow/svelte": "^0.0.12",
|
||||
"svelte": "^4.0.5",
|
||||
"svelte-dnd-action": "^0.9.25",
|
||||
"svelvet": "^8.1.0",
|
||||
|
@ -466,6 +467,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-1.0.8.tgz",
|
||||
"integrity": "sha512-HQ6aJlYpWLq1f9AiApJl0aOIXlJUtuhBOYfSfv5rt3XNYkCBveojtnL6FvOVpJ2gEJ2wqgMW8xOHkLVYAbXghg=="
|
||||
},
|
||||
"node_modules/@svelte-put/shortcut": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-3.0.0.tgz",
|
||||
"integrity": "sha512-nZg3pwpTi9wUsvQPlqOzEsxZcF2jmY5j+VBq/20IUjjd2OpM92XqZAga0PCCjE6OuEobOt58UMnC2QZgOvk0tQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@sveltejs/vite-plugin-svelte": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.5.tgz",
|
||||
|
@ -505,12 +512,300 @@
|
|||
"vite": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz",
|
||||
"integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-array": "*",
|
||||
"@types/d3-axis": "*",
|
||||
"@types/d3-brush": "*",
|
||||
"@types/d3-chord": "*",
|
||||
"@types/d3-color": "*",
|
||||
"@types/d3-contour": "*",
|
||||
"@types/d3-delaunay": "*",
|
||||
"@types/d3-dispatch": "*",
|
||||
"@types/d3-drag": "*",
|
||||
"@types/d3-dsv": "*",
|
||||
"@types/d3-ease": "*",
|
||||
"@types/d3-fetch": "*",
|
||||
"@types/d3-force": "*",
|
||||
"@types/d3-format": "*",
|
||||
"@types/d3-geo": "*",
|
||||
"@types/d3-hierarchy": "*",
|
||||
"@types/d3-interpolate": "*",
|
||||
"@types/d3-path": "*",
|
||||
"@types/d3-polygon": "*",
|
||||
"@types/d3-quadtree": "*",
|
||||
"@types/d3-random": "*",
|
||||
"@types/d3-scale": "*",
|
||||
"@types/d3-scale-chromatic": "*",
|
||||
"@types/d3-selection": "*",
|
||||
"@types/d3-shape": "*",
|
||||
"@types/d3-time": "*",
|
||||
"@types/d3-time-format": "*",
|
||||
"@types/d3-timer": "*",
|
||||
"@types/d3-transition": "*",
|
||||
"@types/d3-zoom": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz",
|
||||
"integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-axis": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.2.tgz",
|
||||
"integrity": "sha512-uGC7DBh0TZrU/LY43Fd8Qr+2ja1FKmH07q2FoZFHo1eYl8aj87GhfVoY1saJVJiq24rp1+wpI6BvQJMKgQm8oA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-brush": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.2.tgz",
|
||||
"integrity": "sha512-2TEm8KzUG3N7z0TrSKPmbxByBx54M+S9lHoP2J55QuLU0VSQ9mE96EJSAOVNEqd1bbynMjeTS9VHmz8/bSw8rA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-chord": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.2.tgz",
|
||||
"integrity": "sha512-abT/iLHD3sGZwqMTX1TYCMEulr+wBd0SzyOQnjYNLp7sngdOHYtNkMRI5v3w5thoN+BWtlHVDx2Osvq6fxhZWw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-contour": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.2.tgz",
|
||||
"integrity": "sha512-k6/bGDoAGJZnZWaKzeB+9glgXCYGvh6YlluxzBREiVo8f/X2vpTEdgPy9DN7Z2i42PZOZ4JDhVdlTSTSkLDPlQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-array": "*",
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-delaunay": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz",
|
||||
"integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-dispatch": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.2.tgz",
|
||||
"integrity": "sha512-rxN6sHUXEZYCKV05MEh4z4WpPSqIw+aP7n9ZN6WYAAvZoEAghEK1WeVZMZcHRBwyaKflU43PCUAJNjFxCzPDjg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-drag": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.2.tgz",
|
||||
"integrity": "sha512-qmODKEDvyKWVHcWWCOVcuVcOwikLVsyc4q4EBJMREsoQnR2Qoc2cZQUyFUPgO9q4S3qdSqJKBsuefv+h0Qy+tw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-dsv": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
"integrity": "sha512-76pBHCMTvPLt44wFOieouXcGXWOF0AJCceUvaFkxSZEu4VDUdv93JfpMa6VGNFs01FHfuP4a5Ou68eRG1KBfTw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz",
|
||||
"integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-fetch": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.2.tgz",
|
||||
"integrity": "sha512-gllwYWozWfbep16N9fByNBDTkJW/SyhH6SGRlXloR7WdtAaBui4plTP+gbUgiEot7vGw/ZZop1yDZlgXXSuzjA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-dsv": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-force": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.4.tgz",
|
||||
"integrity": "sha512-q7xbVLrWcXvSBBEoadowIUJ7sRpS1yvgMWnzHJggFy5cUZBq2HZL5k/pBSm0GdYWS1vs5/EDwMjSKF55PDY4Aw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-format": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz",
|
||||
"integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-geo": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.3.tgz",
|
||||
"integrity": "sha512-bK9uZJS3vuDCNeeXQ4z3u0E7OeJZXjUgzFdSOtNtMCJCLvDtWDwfpRVWlyt3y8EvRzI0ccOu9xlMVirawolSCw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-hierarchy": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
|
||||
"integrity": "sha512-9hjRTVoZjRFR6xo8igAJyNXQyPX6Aq++Nhb5ebrUF414dv4jr2MitM2fWiOY475wa3Za7TOS2Gh9fmqEhLTt0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz",
|
||||
"integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-polygon": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz",
|
||||
"integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-quadtree": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz",
|
||||
"integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-random": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz",
|
||||
"integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz",
|
||||
"integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-scale-chromatic": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
|
||||
"integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-selection": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.5.tgz",
|
||||
"integrity": "sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz",
|
||||
"integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz",
|
||||
"integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-time-format": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz",
|
||||
"integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz",
|
||||
"integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/d3-transition": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.3.tgz",
|
||||
"integrity": "sha512-/S90Od8Id1wgQNvIA8iFv9jRhCiZcGhPd2qX0bKF/PS+y0W5CrXKgIiELd2CvG1mlQrWK/qlYh3VxicqG1ZvgA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-zoom": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.3.tgz",
|
||||
"integrity": "sha512-OWk1yYIIWcZ07+igN6BeoG6rqhnJ/pYe+R1qWFM2DtW49zsoSjgb9G5xB0ZXA8hh2jAzey1XuRmMSoXdKw8MDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3-interpolate": "*",
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
|
||||
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
|
||||
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@xyflow/svelte": {
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@xyflow/svelte/-/svelte-0.0.12.tgz",
|
||||
"integrity": "sha512-BXwOauzjInjPBdJb1GaKVCYIxyznQKzuEltnPb3i3/EO9GJHx1xcD/c8aBUBBnAG7EWBKtuiyDGk/EBW8MEmsg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@svelte-put/shortcut": "^3.0.0",
|
||||
"@xyflow/system": "0.0.2",
|
||||
"classcat": "^5.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@xyflow/system": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.2.tgz",
|
||||
"integrity": "sha512-xtDf35E4Bwmt9IutmhNLna/vMNN3uRA/kwYUYTAifnWSCUaKENBqMOX4xAGNljBJX1yjo7FNlMRgn347raN69Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/d3-drag": "^3.0.1",
|
||||
"@types/d3-selection": "^3.0.3",
|
||||
"@types/d3-zoom": "^3.0.1",
|
||||
"d3-drag": "^3.0.0",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
||||
|
@ -541,6 +836,12 @@
|
|||
"dequal": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/classcat": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.4.tgz",
|
||||
"integrity": "sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/code-red": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
||||
|
@ -567,6 +868,111 @@
|
|||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||
"@xyflow/svelte": "^0.0.12",
|
||||
"svelte": "^4.0.5",
|
||||
"svelte-dnd-action": "^0.9.25",
|
||||
"svelvet": "^8.1.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { inputs, addInput, output, filters } from "./stores.js";
|
||||
import { addNode, nodes, inputs, output, filters, previewCommand } from "./stores.js";
|
||||
import Input from "./Input.svelte";
|
||||
import Output from "./Output.svelte";
|
||||
import Filter from "./Filter.svelte";
|
||||
|
@ -26,7 +26,7 @@
|
|||
let commandRef;
|
||||
|
||||
function newInput() {
|
||||
addInput("punch.mp4");
|
||||
addNode({ name: "punch.mp4" }, "input");
|
||||
}
|
||||
|
||||
function render() {
|
||||
|
@ -81,38 +81,7 @@
|
|||
ffmpegLoaded = true;
|
||||
}
|
||||
|
||||
function updateCommand() {
|
||||
console.log($inputs);
|
||||
const cInputs = $inputs.map((i) => `-i ${i.name}`).join(" ");
|
||||
|
||||
const cOutput = $output;
|
||||
|
||||
const cFilters = $filters.map(makeFilterArgs).join(",");
|
||||
|
||||
let out = `ffmpeg ${cInputs}`;
|
||||
|
||||
if (cFilters) out += ` -filter_complex "${cFilters}"`;
|
||||
|
||||
out += ` ${cOutput}`;
|
||||
|
||||
command = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
function makeFilterArgs(f) {
|
||||
let fCommand = f.name;
|
||||
if (f.params && f.params.length > 0) {
|
||||
let params = f.params
|
||||
.map((p) => {
|
||||
if (p.value === "" || p.value === null || p.value === p.default) return null;
|
||||
return `${p.name}=${p.value}`;
|
||||
})
|
||||
.filter((p) => p !== null)
|
||||
.join(":");
|
||||
if (params) fCommand += "=" + params;
|
||||
}
|
||||
return fCommand;
|
||||
}
|
||||
|
||||
function commandList() {
|
||||
let command = [];
|
||||
|
@ -141,9 +110,9 @@
|
|||
filters.set(e.detail.items);
|
||||
}
|
||||
|
||||
inputs.subscribe(updateCommand);
|
||||
output.subscribe(updateCommand);
|
||||
filters.subscribe(updateCommand);
|
||||
// inputs.subscribe(updateCommand);
|
||||
// output.subscribe(updateCommand);
|
||||
// filters.subscribe(updateCommand);
|
||||
|
||||
onMount(async () => {
|
||||
loadFFmpeg();
|
||||
|
@ -159,16 +128,15 @@
|
|||
export and add some filters, and then hit "render" to preview the output in browser. Note: this
|
||||
is a work in progress, many things may still be broken! Only audio to audio and video to video
|
||||
filters are included. If it hangs/crashes refresh the page. Post issues/feedback to
|
||||
<a href="https://github.com/antiboredom/ffmpeg-explorer/" target="_blank"
|
||||
>GitHub</a
|
||||
>. By <a href="https://lav.io" target="_blank">Sam Lavigne</a>.
|
||||
<a href="https://github.com/antiboredom/ffmpeg-explorer/" target="_blank">GitHub</a>. By
|
||||
<a href="https://lav.io" target="_blank">Sam Lavigne</a>.
|
||||
</p>
|
||||
</section>
|
||||
<!-- {message} -->
|
||||
<section class="command">
|
||||
<h3>Output Command</h3>
|
||||
<div class="inner-command">
|
||||
<textarea readonly class="actual-command" bind:this={commandRef}>{command}</textarea>
|
||||
<textarea readonly class="actual-command" bind:this={commandRef}>{$previewCommand}</textarea>
|
||||
<div>
|
||||
<button on:click={copyCommand}>Copy Command</button>
|
||||
</div>
|
||||
|
@ -180,8 +148,10 @@
|
|||
<h3>Inputs</h3>
|
||||
<button on:click={newInput}>Add Input</button>
|
||||
</div>
|
||||
{#each $inputs as inp, index}
|
||||
<Input bind:filename={inp.name} id={inp.id} {index} />
|
||||
{#each $nodes as node, index}
|
||||
{#if node.nodeType === "input"}
|
||||
<Input bind:filename={node.data.name} id={node.id} {index} />
|
||||
{/if}
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
|
@ -212,7 +182,11 @@
|
|||
|
||||
<section class="output">
|
||||
<h3>Output</h3>
|
||||
<Output bind:filename={$output} />
|
||||
{#each $nodes as node}
|
||||
{#if node.nodeType==="output"}
|
||||
<Output bind:filename={node.data.name} />
|
||||
{/if}
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<section class="filters">
|
||||
|
@ -227,18 +201,20 @@
|
|||
on:consider={handleFilterSort}
|
||||
on:finalize={handleFilterSort}
|
||||
>
|
||||
{#each $filters as f (f.id)}
|
||||
<div class="filter">
|
||||
<Filter bind:filter={f} />
|
||||
</div>
|
||||
{#each $nodes as f (f.id)}
|
||||
{#if f.nodeType === "filter"}
|
||||
<div class="filter">
|
||||
<Filter bind:filter={f.data} />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="graph">
|
||||
<Graph />
|
||||
</section>
|
||||
<section class="graph">
|
||||
<Graph />
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { filters, removeFilter } from "./stores.js";
|
||||
import { filters, removeNode } from "./stores.js";
|
||||
|
||||
export let filter = {
|
||||
name: "",
|
||||
|
@ -10,7 +10,7 @@
|
|||
let show = false;
|
||||
|
||||
function remove() {
|
||||
removeFilter(filter.id);
|
||||
removeNode(filter.id);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import uFuzzy from "@leeoniya/ufuzzy";
|
||||
import FILTERS from "./filters.json";
|
||||
import { addFilter } from "./stores.js";
|
||||
import { addNode } from "./stores.js";
|
||||
|
||||
export let select = "video";
|
||||
$: selectedFilters = selectFilters(select);
|
||||
|
@ -25,7 +25,7 @@
|
|||
}
|
||||
|
||||
function add(f) {
|
||||
addFilter(f);
|
||||
addNode(f, "filter");
|
||||
}
|
||||
|
||||
function update() {
|
||||
|
|
277
src/Graph.svelte
277
src/Graph.svelte
|
@ -1,201 +1,94 @@
|
|||
<script>
|
||||
import { inputs, output, filters } from "./stores.js";
|
||||
import { Anchor, Node, Svelvet, Minimap, Controls } from "svelvet";
|
||||
import { nodes, edges, command } from "./stores.js";
|
||||
import { SvelteFlow, Controls, Background, BackgroundVariant, MiniMap } from "@xyflow/svelte";
|
||||
import Node from "./nodes/Node.svelte";
|
||||
|
||||
// let nodes = [];
|
||||
// for (let inp of $inputs) {
|
||||
// nodes.push({item: inp});
|
||||
// }
|
||||
//
|
||||
// for (let filter of $filters) {
|
||||
// nodes.push({item: filter});
|
||||
// }
|
||||
let edges = [];
|
||||
let command = '';
|
||||
const nodeTypes = {
|
||||
ffmpeg: Node,
|
||||
};
|
||||
|
||||
function makeCommand() {
|
||||
for (let e of edges) {
|
||||
const [from, to] = e;
|
||||
const [fromAnchorId, fromNodeId] = from.split("/");
|
||||
const [toAnchorId, toNodeId] = to.split("/");
|
||||
}
|
||||
console.log($nodes);
|
||||
|
||||
// const nodes = writable([
|
||||
// {
|
||||
// id: "1",
|
||||
// type: "ffmpeg",
|
||||
// data: { label: "test.mp4", inputs: [], outputs: ["v", "a"] },
|
||||
// position: { x: 0, y: 0 },
|
||||
// },
|
||||
// {
|
||||
// id: "2",
|
||||
// type: "ffmpeg",
|
||||
// data: { label: "filter", inputs: ["v"], outputs: ["v"] },
|
||||
// position: { x: 0, y: 150 },
|
||||
// },
|
||||
// {
|
||||
// id: "3",
|
||||
// type: "ffmpeg",
|
||||
// data: { label: "output.mp4", inputs: ["v", "a"], outputs: [] },
|
||||
// position: { x: 100, y: 20 },
|
||||
// },
|
||||
// ]);
|
||||
|
||||
// same for edges
|
||||
// const edges = writable([
|
||||
// {
|
||||
// id: "1-2",
|
||||
// type: "default",
|
||||
// source: "1",
|
||||
// sourceHandle: "v_0",
|
||||
// targetHandle: "v_0",
|
||||
// target: "2",
|
||||
// label: "Edge Text",
|
||||
// },
|
||||
// {
|
||||
// id: "2-3",
|
||||
// type: "default",
|
||||
// source: "2",
|
||||
// target: "3",
|
||||
// sourceHandle: "v_0",
|
||||
// targetHandle: "v_0",
|
||||
// label: "Edge Text",
|
||||
// },
|
||||
// ]);
|
||||
|
||||
const defaultEdgeOptions = {
|
||||
deletable: true,
|
||||
};
|
||||
|
||||
function onEdgeUpdate(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
function onEdgeUpdateStart(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
function onEdgeUpdateEnd(e) {
|
||||
console.log(e);
|
||||
}
|
||||
function onMoveStart(e) {
|
||||
console.log(e);
|
||||
}
|
||||
function onConnect(e){
|
||||
console.log('connect', e);
|
||||
}
|
||||
|
||||
function countInputs(f) {
|
||||
const [ins, outs] = f.type.split("->");
|
||||
if (ins == "N") return 1;
|
||||
return ins.length;
|
||||
}
|
||||
|
||||
function countCons(f) {
|
||||
const [ins, outs] = f.type.split("->");
|
||||
return { in: ins.split(""), out: outs.split("") };
|
||||
}
|
||||
|
||||
function countOutputs(f) {
|
||||
const [ins, outs] = f.type.split("->");
|
||||
if (outs == "N") return 1;
|
||||
return outs.length;
|
||||
}
|
||||
|
||||
function onConnect(e) {
|
||||
const sourceAnchor = e.detail.sourceAnchor;
|
||||
const targetAnchor = e.detail.targetAnchor;
|
||||
const sourceNode = e.detail.sourceNode;
|
||||
const targetNode = e.detail.targetNode;
|
||||
// console.log(e);
|
||||
// console.log(sourceNode.id, "->", targetNode.id)
|
||||
// console.log(sourceAnchor.id, "->", targetAnchor.id)
|
||||
edges.push([sourceAnchor.id, targetAnchor.id])
|
||||
edges = edges;
|
||||
makeCommand();
|
||||
}
|
||||
|
||||
function onDisconnect(e) {
|
||||
const sourceAnchor = e.detail.sourceAnchor;
|
||||
const targetAnchor = e.detail.targetAnchor;
|
||||
const sourceNode = e.detail.sourceNode;
|
||||
const targetNode = e.detail.targetNode;
|
||||
|
||||
// console.log(sourceNode.id, "-/>", targetNode.id)
|
||||
// console.log(sourceAnchor.id, "-/>", targetAnchor.id)
|
||||
|
||||
const index = edges.findIndex((e) => e[0] === sourceAnchor.id && e[1] === targetAnchor.id);
|
||||
edges.splice(index, 1);
|
||||
edges = edges;
|
||||
makeCommand();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Svelvet
|
||||
id="my-canvas"
|
||||
width={800}
|
||||
height={500}
|
||||
snapTo={25}
|
||||
on:disconnection={onDisconnect}
|
||||
on:connection={onConnect}
|
||||
>
|
||||
{#each $inputs as inp, index}
|
||||
<Node inputs={0} outputs={2} id={"input_" + inp.id}>
|
||||
<div class="node">
|
||||
<div class="header">
|
||||
{inp.name}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="output-anchors">
|
||||
<Anchor id={"video"} let:linked let:connecting let:hovering output>
|
||||
<div class:linked class:hovering class:connecting class="anchor video">v</div>
|
||||
</Anchor>
|
||||
<Anchor id={"audio"} let:linked let:connecting let:hovering output>
|
||||
<div class:linked class:hovering class:connecting class="anchor audio">a</div>
|
||||
</Anchor>
|
||||
</div>
|
||||
</Node>
|
||||
{/each}
|
||||
|
||||
{#each $filters as f, index}
|
||||
<Node inputs={countInputs(f)} outputs={countOutputs(f)} id={"filter_" + f.id}>
|
||||
<div class="node">
|
||||
<div class="header">
|
||||
{f.name}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="input-anchors">
|
||||
{#each countCons(f).in as inp, index}
|
||||
<Anchor id={"in_" + inp + "_" + index} let:linked let:connecting let:hovering input>
|
||||
<div class:linked class:hovering class:connecting class="anchor in {inp}">{inp}</div>
|
||||
</Anchor>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="output-anchors">
|
||||
{#each countCons(f).out as out}
|
||||
<Anchor id={"out_" + out + "_" + index} let:linked let:connecting let:hovering output>
|
||||
<div class:linked class:hovering class:connecting class="anchor in {out}">{out}</div>
|
||||
</Anchor>
|
||||
{/each}
|
||||
</div>
|
||||
</Node>
|
||||
{/each}
|
||||
|
||||
<Node inputs={2} outputs="0" id="output" position={{ x: 600, y: 250 }}>
|
||||
<div class="node">
|
||||
<div class="header">
|
||||
{$output}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="input-anchors">
|
||||
<Anchor id={"output_video"} let:linked let:connecting let:hovering input>
|
||||
<div class:linked class:hovering class:connecting class="anchor video">v</div>
|
||||
</Anchor>
|
||||
<Anchor id={"output_audio"} let:linked let:connecting let:hovering input>
|
||||
<div class:linked class:hovering class:connecting class="anchor audio">a</div>
|
||||
</Anchor>
|
||||
</div>
|
||||
</Node>
|
||||
<Controls />
|
||||
</Svelvet>
|
||||
|
||||
<div>
|
||||
{#each edges as e}
|
||||
<p>
|
||||
{e[0]} =======> {e[1]}
|
||||
</p>
|
||||
{/each}
|
||||
<div style="width: 900px; height: 500px;">
|
||||
<SvelteFlow
|
||||
{nodeTypes}
|
||||
{nodes}
|
||||
{edges}
|
||||
snapGrid={[10, 10]}
|
||||
fitView
|
||||
>
|
||||
<Controls />
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
</SvelteFlow>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.node {
|
||||
box-sizing: border-box;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
background-color: #fff;
|
||||
border: 1px solid var(--b1);
|
||||
font: 12px Times, serif;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.output-anchors {
|
||||
position: absolute;
|
||||
right: -16px;
|
||||
top: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.input-anchors {
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
top: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.anchor {
|
||||
background-color: #fff;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
line-height: 12px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-family: Times, serif;
|
||||
border: solid 1px white;
|
||||
border-color: var(--b1);
|
||||
}
|
||||
.hovering {
|
||||
scale: 1.2;
|
||||
}
|
||||
.linked {
|
||||
background-color: rgb(17, 214, 17) !important;
|
||||
}
|
||||
.connecting {
|
||||
background-color: goldenrod;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
{JSON.stringify(command)}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
<script>
|
||||
import { inputs, output, filters } from "./stores.js";
|
||||
import { Anchor, Node, Svelvet, Minimap, Controls } from "svelvet";
|
||||
|
||||
// let nodes = [];
|
||||
// for (let inp of $inputs) {
|
||||
// nodes.push({item: inp});
|
||||
// }
|
||||
//
|
||||
// for (let filter of $filters) {
|
||||
// nodes.push({item: filter});
|
||||
// }
|
||||
let edges = [];
|
||||
let command = '';
|
||||
|
||||
function makeCommand() {
|
||||
for (let e of edges) {
|
||||
const [from, to] = e;
|
||||
const [fromAnchorId, fromNodeId] = from.split("/");
|
||||
const [toAnchorId, toNodeId] = to.split("/");
|
||||
}
|
||||
}
|
||||
|
||||
function countInputs(f) {
|
||||
const [ins, outs] = f.type.split("->");
|
||||
if (ins == "N") return 1;
|
||||
return ins.length;
|
||||
}
|
||||
|
||||
function countCons(f) {
|
||||
const [ins, outs] = f.type.split("->");
|
||||
return { in: ins.split(""), out: outs.split("") };
|
||||
}
|
||||
|
||||
function countOutputs(f) {
|
||||
const [ins, outs] = f.type.split("->");
|
||||
if (outs == "N") return 1;
|
||||
return outs.length;
|
||||
}
|
||||
|
||||
function onConnect(e) {
|
||||
const sourceAnchor = e.detail.sourceAnchor;
|
||||
const targetAnchor = e.detail.targetAnchor;
|
||||
const sourceNode = e.detail.sourceNode;
|
||||
const targetNode = e.detail.targetNode;
|
||||
// console.log(e);
|
||||
// console.log(sourceNode.id, "->", targetNode.id)
|
||||
// console.log(sourceAnchor.id, "->", targetAnchor.id)
|
||||
edges.push([sourceAnchor.id, targetAnchor.id])
|
||||
edges = edges;
|
||||
makeCommand();
|
||||
}
|
||||
|
||||
function onDisconnect(e) {
|
||||
const sourceAnchor = e.detail.sourceAnchor;
|
||||
const targetAnchor = e.detail.targetAnchor;
|
||||
const sourceNode = e.detail.sourceNode;
|
||||
const targetNode = e.detail.targetNode;
|
||||
|
||||
// console.log(sourceNode.id, "-/>", targetNode.id)
|
||||
// console.log(sourceAnchor.id, "-/>", targetAnchor.id)
|
||||
|
||||
const index = edges.findIndex((e) => e[0] === sourceAnchor.id && e[1] === targetAnchor.id);
|
||||
edges.splice(index, 1);
|
||||
edges = edges;
|
||||
makeCommand();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Svelvet
|
||||
id="my-canvas"
|
||||
width={800}
|
||||
height={500}
|
||||
snapTo={25}
|
||||
on:disconnection={onDisconnect}
|
||||
on:connection={onConnect}
|
||||
>
|
||||
{#each $inputs as inp, index}
|
||||
<Node inputs={0} outputs={2} id={"input_" + inp.id}>
|
||||
<div class="node">
|
||||
<div class="header">
|
||||
{inp.name}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="output-anchors">
|
||||
<Anchor id={"video"} let:linked let:connecting let:hovering output>
|
||||
<div class:linked class:hovering class:connecting class="anchor video">v</div>
|
||||
</Anchor>
|
||||
<Anchor id={"audio"} let:linked let:connecting let:hovering output>
|
||||
<div class:linked class:hovering class:connecting class="anchor audio">a</div>
|
||||
</Anchor>
|
||||
</div>
|
||||
</Node>
|
||||
{/each}
|
||||
|
||||
{#each $filters as f, index}
|
||||
<Node inputs={countInputs(f)} outputs={countOutputs(f)} id={"filter_" + f.id}>
|
||||
<div class="node">
|
||||
<div class="header">
|
||||
{f.name}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="input-anchors">
|
||||
{#each countCons(f).in as inp, index}
|
||||
<Anchor id={"in_" + inp + "_" + index} let:linked let:connecting let:hovering input>
|
||||
<div class:linked class:hovering class:connecting class="anchor in {inp}">{inp}</div>
|
||||
</Anchor>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="output-anchors">
|
||||
{#each countCons(f).out as out}
|
||||
<Anchor id={"out_" + out + "_" + index} let:linked let:connecting let:hovering output>
|
||||
<div class:linked class:hovering class:connecting class="anchor in {out}">{out}</div>
|
||||
</Anchor>
|
||||
{/each}
|
||||
</div>
|
||||
</Node>
|
||||
{/each}
|
||||
|
||||
<Node inputs={2} outputs="0" id="output" position={{ x: 600, y: 250 }}>
|
||||
<div class="node">
|
||||
<div class="header">
|
||||
{$output}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="input-anchors">
|
||||
<Anchor id={"output_video"} let:linked let:connecting let:hovering input>
|
||||
<div class:linked class:hovering class:connecting class="anchor video">v</div>
|
||||
</Anchor>
|
||||
<Anchor id={"output_audio"} let:linked let:connecting let:hovering input>
|
||||
<div class:linked class:hovering class:connecting class="anchor audio">a</div>
|
||||
</Anchor>
|
||||
</div>
|
||||
</Node>
|
||||
<Controls />
|
||||
</Svelvet>
|
||||
|
||||
<div>
|
||||
{#each edges as e}
|
||||
<p>
|
||||
{e[0]} =======> {e[1]}
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.node {
|
||||
box-sizing: border-box;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
background-color: #fff;
|
||||
border: 1px solid var(--b1);
|
||||
font: 12px Times, serif;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.output-anchors {
|
||||
position: absolute;
|
||||
right: -16px;
|
||||
top: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.input-anchors {
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
top: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.anchor {
|
||||
background-color: #fff;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
line-height: 12px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-family: Times, serif;
|
||||
border: solid 1px white;
|
||||
border-color: var(--b1);
|
||||
}
|
||||
.hovering {
|
||||
scale: 1.2;
|
||||
}
|
||||
.linked {
|
||||
background-color: rgb(17, 214, 17) !important;
|
||||
}
|
||||
.connecting {
|
||||
background-color: goldenrod;
|
||||
}
|
||||
</style>
|
|
@ -1,12 +1,11 @@
|
|||
<script>
|
||||
import { removeInput } from "./stores.js";
|
||||
import { removeNode } from "./stores.js";
|
||||
export let filename = "punch.mp4";
|
||||
export let id;
|
||||
export let index;
|
||||
|
||||
function remove() {
|
||||
console.log(id);
|
||||
removeInput(id);
|
||||
removeNode(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { Handle, Position } from "@xyflow/svelte";
|
||||
|
||||
export let data = { name: "", inputs: [], outputs: [], onChange: () => {} };
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{data.name}
|
||||
</div>
|
||||
{#each data.inputs as inp, index}
|
||||
<Handle type="target" position={Position.Left} id={inp + "_" + index} style="top: {index*10}px; background-color: {inp == 'v' ? 'blue' : 'red'}" on:connect />
|
||||
{/each}
|
||||
{#each data.outputs as out, index}
|
||||
<Handle type="source" id={out + "_" + index} position={Position.Right} style="top: {index*10}px; background-color: {out == 'v' ? 'blue' : 'red'}" on:connect />
|
||||
{/each}
|
||||
<!-- <Handle type="source" position={Position.Right} id="a" style="top: 10px;" on:connect /> -->
|
||||
<!-- <Handle -->
|
||||
<!-- type="source" -->
|
||||
<!-- position={Position.Right} -->
|
||||
<!-- id="b" -->
|
||||
<!-- style="top: auto; bottom: 10px;" -->
|
||||
<!-- on:connect -->
|
||||
<!-- /> -->
|
187
src/stores.js
187
src/stores.js
|
@ -1,55 +1,148 @@
|
|||
import { v4 as uuidv4 } from "uuid";
|
||||
import { writable } from 'svelte/store';
|
||||
import { writable, derived, get } from "svelte/store";
|
||||
|
||||
export const inputs = writable([{name: "punch.mp4", id: uuidv4()}]);
|
||||
export const output = writable("out.mp4");
|
||||
export const filters = writable([]);
|
||||
// export const inputs = writable([]);
|
||||
// export const output = writable("out.mp4");
|
||||
// export const filters = writable([]);
|
||||
export const nodes = writable([]);
|
||||
export const edges = writable([]);
|
||||
|
||||
export function addFilter(f) {
|
||||
const newFilter = { ...f, filterId: f.id, id: uuidv4() };
|
||||
if (f.params) {
|
||||
newFilter.params = f.params.map((p) => {
|
||||
p.value = null;
|
||||
if (p.default != null) p.value = p.default;
|
||||
return p;
|
||||
});
|
||||
}
|
||||
filters.update((filts) => {
|
||||
filts.push(newFilter)
|
||||
return filts;
|
||||
})
|
||||
addNode({ name: "punch.mp4" }, "input");
|
||||
addNode({ name: "out.mp4" }, "output");
|
||||
|
||||
function makeFilterArgs(f) {
|
||||
let fCommand = f.name;
|
||||
if (f.params && f.params.length > 0) {
|
||||
let params = f.params
|
||||
.map((p) => {
|
||||
if (p.value === "" || p.value === null || p.value === p.default) return null;
|
||||
return `${p.name}=${p.value}`;
|
||||
})
|
||||
.filter((p) => p !== null)
|
||||
.join(":");
|
||||
if (params) fCommand += "=" + params;
|
||||
}
|
||||
return fCommand;
|
||||
}
|
||||
|
||||
export function removeFilter(id) {
|
||||
filters.update((filts) => {
|
||||
const index = filts.findIndex((f) => f.id === id);
|
||||
filts.splice(index, 1);
|
||||
return filts;
|
||||
});
|
||||
export const command = derived(edges, ($edges) => {
|
||||
console.log($edges);
|
||||
return $edges;
|
||||
});
|
||||
|
||||
export const previewCommand = derived(nodes, ($nodes) => {
|
||||
const cInputs = $nodes
|
||||
.filter((n) => n.nodeType === "input")
|
||||
.map((i) => `-i ${i.data.name}`)
|
||||
.join(" ");
|
||||
|
||||
const cOutput = $nodes
|
||||
.filter((n) => n.nodeType === "output")
|
||||
.map((i) => `${i.data.name}`)
|
||||
.join(" ");
|
||||
|
||||
const cFilters = $nodes
|
||||
.filter((n) => n.nodeType === "filter")
|
||||
.map((n) => n.data)
|
||||
.map(makeFilterArgs)
|
||||
.join(",");
|
||||
|
||||
let out = `ffmpeg ${cInputs}`;
|
||||
|
||||
if (cFilters) out += ` -filter_complex "${cFilters}"`;
|
||||
|
||||
out += ` ${cOutput}`;
|
||||
return out;
|
||||
});
|
||||
|
||||
export const inputs = derived(nodes, ($nodes) => {
|
||||
return $nodes.filter((n) => n.nodeType === "input").map((n) => n.data);
|
||||
});
|
||||
|
||||
export const filters = derived(nodes, ($nodes) => {
|
||||
return $nodes.filter((n) => n.nodeType === "filter").map((n) => n.data);
|
||||
});
|
||||
|
||||
export const output = derived(nodes, ($nodes) => {
|
||||
return $nodes.filter((n) => n.nodeType === "output").map((n) => n.data);
|
||||
});
|
||||
|
||||
export function addNode(data, type) {
|
||||
let ins = [];
|
||||
let outs = [];
|
||||
|
||||
if (type === "input") {
|
||||
outs = ["v", "a"];
|
||||
} else if (type === "output") {
|
||||
ins = ["v", "a"];
|
||||
} else if (type === "filter") {
|
||||
const [_ins, _outs] = data.type.split("->");
|
||||
ins = _ins.toLowerCase().split("");
|
||||
outs = _outs.toLowerCase().split("");
|
||||
if (data.params) {
|
||||
data.params = data.params.map((p) => {
|
||||
p.value = null;
|
||||
if (p.default != null) p.value = p.default;
|
||||
return p;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
|
||||
const w = 100;
|
||||
const h = 50;
|
||||
const margin = 10;
|
||||
|
||||
const existing = get(nodes);
|
||||
|
||||
if (type === "input") {
|
||||
const inps = existing.filter((n) => n.nodeType === "input");
|
||||
y = inps.length * h;
|
||||
} else if (type === "filter") {
|
||||
const flts = existing.filter((n) => n.nodeType === "filter");
|
||||
x = (flts.length + 1) * w;
|
||||
} else if (type === "output") {
|
||||
x = 500;
|
||||
}
|
||||
|
||||
data.inputs = ins;
|
||||
data.outputs = outs;
|
||||
|
||||
let node = {
|
||||
id: uuidv4(),
|
||||
type: "ffmpeg",
|
||||
data: data,
|
||||
nodeType: type,
|
||||
position: { x, y },
|
||||
};
|
||||
|
||||
nodes.update((n) => {
|
||||
n.push(node);
|
||||
return n;
|
||||
});
|
||||
|
||||
edges.update((_edges) => {
|
||||
console.log('EXISTING', existing);
|
||||
const target = existing[existing.length -2];
|
||||
if (!target) return _edges;
|
||||
const newEdge = {
|
||||
id: uuidv4(),
|
||||
type: "default",
|
||||
source: node.id,
|
||||
target: target.id,
|
||||
};
|
||||
console.log("NEW EDGE", newEdge);
|
||||
_edges.push(newEdge);
|
||||
return _edges;
|
||||
});
|
||||
}
|
||||
|
||||
export function addOutput(f) {
|
||||
|
||||
}
|
||||
|
||||
export function removeOutput(f) {
|
||||
|
||||
}
|
||||
|
||||
export function addInput(f) {
|
||||
const newInput = {name: f, id: uuidv4()}
|
||||
inputs.update((inps) => {
|
||||
inps.push(newInput);
|
||||
return inps;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function removeInput(id) {
|
||||
inputs.update((inps) => {
|
||||
const index = inps.findIndex((f) => f.id === id);
|
||||
inps.splice(index, 1);
|
||||
return inps;
|
||||
});
|
||||
|
||||
export function removeNode(id) {
|
||||
nodes.update((_nodes) => {
|
||||
const index = _nodes.findIndex((n) => n.id === id);
|
||||
_nodes.splice(index, 1);
|
||||
return _nodes;
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue