diff --git a/README.md b/README.md index 67920ca8..0eb1734e 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ This readme file is used mostly to document project dependencies and conventions - js-file-download - use-debounce - framer-motion - - reagraph - html-to-image - @tanstack/react-table - @uiw/react-codemirror diff --git a/rsconcept/frontend/package-lock.json b/rsconcept/frontend/package-lock.json index ca05c0c7..7d79714b 100644 --- a/rsconcept/frontend/package-lock.json +++ b/rsconcept/frontend/package-lock.json @@ -32,7 +32,6 @@ "react-tooltip": "^5.28.0", "react-zoom-pan-pinch": "^3.6.1", "reactflow": "^11.11.4", - "reagraph": "^4.20.1", "use-debounce": "^10.0.4" }, "devDependencies": { @@ -2196,12 +2195,6 @@ "@lezer/common": "^1.0.0" } }, - "node_modules/@mediapipe/tasks-vision": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.8.tgz", - "integrity": "sha512-Rp7ll8BHrKB3wXaRFKhrltwZl1CiXGdibPxuWXvqGnKTnv8fqa/nvftYNuSbf+pbJWKYCXdBtYTITdAUTGGh0Q==", - "license": "Apache-2.0" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2251,148 +2244,6 @@ "node": ">=14" } }, - "node_modules/@react-spring/animated": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz", - "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==", - "license": "MIT", - "dependencies": { - "@react-spring/shared": "~9.6.1", - "@react-spring/types": "~9.6.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/core": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz", - "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==", - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~9.6.1", - "@react-spring/rafz": "~9.6.1", - "@react-spring/shared": "~9.6.1", - "@react-spring/types": "~9.6.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/rafz": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz", - "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==", - "license": "MIT" - }, - "node_modules/@react-spring/shared": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz", - "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==", - "license": "MIT", - "dependencies": { - "@react-spring/rafz": "~9.6.1", - "@react-spring/types": "~9.6.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/three": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz", - "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==", - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~9.6.1", - "@react-spring/core": "~9.6.1", - "@react-spring/shared": "~9.6.1", - "@react-spring/types": "~9.6.1" - }, - "peerDependencies": { - "@react-three/fiber": ">=6.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "three": ">=0.126" - } - }, - "node_modules/@react-spring/types": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz", - "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==", - "license": "MIT" - }, - "node_modules/@react-three/fiber": { - "version": "8.13.5", - "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.13.5.tgz", - "integrity": "sha512-x9QdsaB/Wm/6NGvRXQahPPWfn2dQce7Fg3C2r00NNzyDdqRKw32YavL+WEqjZOOd0nvFpzv7FtaKc+VCOTR59w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.17.8", - "@types/react-reconciler": "^0.26.7", - "its-fine": "^1.0.6", - "react-reconciler": "^0.27.0", - "react-use-measure": "^2.1.1", - "scheduler": "^0.21.0", - "suspend-react": "^0.1.3", - "zustand": "^3.7.1" - }, - "peerDependencies": { - "expo": ">=43.0", - "expo-asset": ">=8.4", - "expo-gl": ">=11.0", - "react": ">=18.0", - "react-dom": ">=18.0", - "react-native": ">=0.64", - "three": ">=0.133" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - }, - "expo-asset": { - "optional": true - }, - "expo-gl": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/@react-three/fiber/node_modules/scheduler": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", - "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/@react-three/fiber/node_modules/zustand": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", - "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", - "license": "MIT", - "engines": { - "node": ">=12.7.0" - }, - "peerDependencies": { - "react": ">=16.8" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } - } - }, "node_modules/@reactflow/background": { "version": "11.3.14", "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz", @@ -2807,12 +2658,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@tweenjs/tween.js": { - "version": "23.1.3", - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", - "license": "MIT" - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3117,12 +2962,6 @@ "@types/d3-selection": "*" } }, - "node_modules/@types/draco3d": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz", - "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -3209,12 +3048,6 @@ "undici-types": "~6.19.8" } }, - "node_modules/@types/offscreencanvas": { - "version": "2019.7.3", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", - "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", - "license": "MIT" - }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -3247,15 +3080,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-reconciler": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz", - "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-transition-group": { "version": "4.4.11", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", @@ -3272,38 +3096,12 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/stats.js": { - "version": "0.17.3", - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", - "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", - "license": "MIT" - }, "node_modules/@types/stylis": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", "license": "MIT" }, - "node_modules/@types/three": { - "version": "0.170.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.170.0.tgz", - "integrity": "sha512-CUm2uckq+zkCY7ZbFpviRttY+6f9fvwm6YqSqPfA5K22s9w7R4VnA3rzJse8kHVvuzLcTx+CjNCs2NYe0QFAyg==", - "license": "MIT", - "dependencies": { - "@tweenjs/tween.js": "~23.1.3", - "@types/stats.js": "*", - "@types/webxr": "*", - "@webgpu/types": "*", - "fflate": "~0.8.2", - "meshoptimizer": "~0.18.1" - } - }, - "node_modules/@types/webxr": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.20.tgz", - "integrity": "sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==", - "license": "MIT" - }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -3604,24 +3402,6 @@ "react-dom": ">=16.8.0" } }, - "node_modules/@use-gesture/core": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", - "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", - "license": "MIT" - }, - "node_modules/@use-gesture/react": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", - "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", - "license": "MIT", - "dependencies": { - "@use-gesture/core": "10.3.1" - }, - "peerDependencies": { - "react": ">= 16.8.0" - } - }, "node_modules/@vitejs/plugin-react": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz", @@ -3642,18 +3422,6 @@ "vite": "^4.2.0 || ^5.0.0" } }, - "node_modules/@webgpu/types": { - "version": "0.1.51", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.51.tgz", - "integrity": "sha512-ktR3u64NPjwIViNCck+z9QeyN0iPkQCUOQ07ZCV1RzlkfP+olLTeEZ95O1QHS+v4w9vJeY9xj/uJuSphsHy5rQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@yomguithereal/helpers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@yomguithereal/helpers/-/helpers-1.1.1.tgz", - "integrity": "sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg==", - "license": "MIT" - }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -4145,15 +3913,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, - "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", - "license": "MIT", - "dependencies": { - "require-from-string": "^2.0.2" - } - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4311,15 +4070,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/camera-controls": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.9.0.tgz", - "integrity": "sha512-TpCujnP0vqPppTXXJRYpvIy0xq9Tro6jQf2iYUxlDpPCNxkvE/XGaTuwIxnhINOkVP/ob2CRYXtY3iVYXeMEzA==", - "license": "MIT", - "peerDependencies": { - "three": ">=0.126.1" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001680", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", @@ -4603,24 +4353,6 @@ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "license": "MIT" }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4674,33 +4406,6 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, - "node_modules/ctrl-keys": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ctrl-keys/-/ctrl-keys-1.0.3.tgz", - "integrity": "sha512-Kcb05/xUNra57fxpsLOflECWYbjQEQ9ZuQEthB3cgESN5zMLJ364twA9h2kqz8n06RnTY/+rKWM3UbkOWKeEJg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-binarytree": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d3-binarytree/-/d3-binarytree-1.0.2.tgz", - "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==", - "license": "MIT" - }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", @@ -4741,40 +4446,6 @@ "node": ">=12" } }, - "node_modules/d3-force-3d": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.5.tgz", - "integrity": "sha512-tdwhAhoTYZY/a6eo9nR7HP3xSW/C6XvJTbeRpR92nlPzH6OiE+4MliN9feuSFd0tPtEUo+191qOhCTWx3NYifg==", - "license": "MIT", - "dependencies": { - "d3-binarytree": "1", - "d3-dispatch": "1 - 3", - "d3-octree": "1", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/d3-interpolate": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", @@ -4787,37 +4458,6 @@ "node": ">=12" } }, - "node_modules/d3-octree": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.0.2.tgz", - "integrity": "sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA==", - "license": "MIT" - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/d3-selection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", @@ -4827,30 +4467,6 @@ "node": ">=12" } }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/d3-timer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", @@ -4949,12 +4565,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "license": "MIT" - }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -5048,15 +4658,6 @@ "node": ">=0.4.0" } }, - "node_modules/detect-gpu": { - "version": "5.0.57", - "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.57.tgz", - "integrity": "sha512-iHfsCCAyxYTE0S4ULs52/DhV2W4l+VT9sTnnYLMwgdNlXKks6PSZleZRDmTCLvPXS4Lt30JI/M5QjhUPwwnZfQ==", - "license": "MIT", - "dependencies": { - "webgl-constants": "^1.1.1" - } - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -5114,12 +4715,6 @@ "csstype": "^3.0.2" } }, - "node_modules/draco3d": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", - "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==", - "license": "Apache-2.0" - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -5150,12 +4745,6 @@ "dev": true, "license": "ISC" }, - "node_modules/ellipsize": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/ellipsize/-/ellipsize-0.5.1.tgz", - "integrity": "sha512-0jEAyuIRU6U8MN0S5yUqIrkK/AQWkChh642N3zQuGV57s9bsUWYLc0jJOoDIUkZ2sbEL3ySq8xfq71BvG4q3hw==", - "license": "MIT" - }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -5708,15 +5297,6 @@ "node": ">=0.10.0" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -5835,12 +5415,6 @@ "bser": "2.1.1" } }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -6272,70 +5846,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glodrei": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/glodrei/-/glodrei-0.0.1.tgz", - "integrity": "sha512-DMx6ElCSwh1pR4IyDS3LvyFwZHSCCKCqdqo8P1G7klQtqH6PcOjleduCDsHehDtyYQ1E4dzVeoEzHIL1DIxjag==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.2", - "@mediapipe/tasks-vision": "0.10.8", - "@react-spring/three": "~9.6.1", - "@use-gesture/react": "^10.2.24", - "camera-controls": "^2.4.2", - "cross-env": "^7.0.3", - "detect-gpu": "^5.0.28", - "glsl-noise": "^0.0.0", - "maath": "^0.10.7", - "meshline": "^3.1.6", - "react-composer": "^5.0.3", - "react-merge-refs": "^1.1.0", - "stats-gl": "^2.0.0", - "stats.js": "^0.17.0", - "suspend-react": "^0.1.3", - "three-mesh-bvh": "^0.7.0", - "three-stdlib": "^2.29.4", - "troika-three-text": "^0.47.2", - "tunnel-rat": "^0.1.2", - "utility-types": "^3.10.0", - "uuid": "^9.0.1", - "zustand": "^3.7.1" - }, - "peerDependencies": { - "@react-three/fiber": ">=8.0", - "react": ">=18.0", - "react-dom": ">=18.0", - "three": ">=0.137" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/glodrei/node_modules/zustand": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", - "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", - "license": "MIT", - "engines": { - "node": ">=12.7.0" - }, - "peerDependencies": { - "react": ">=16.8" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } - } - }, - "node_modules/glsl-noise": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", - "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", - "license": "MIT" - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -6363,115 +5873,6 @@ "dev": true, "license": "MIT" }, - "node_modules/graphology": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz", - "integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==", - "license": "MIT", - "dependencies": { - "events": "^3.3.0", - "obliterator": "^2.0.2" - }, - "peerDependencies": { - "graphology-types": ">=0.24.0" - } - }, - "node_modules/graphology-indices": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/graphology-indices/-/graphology-indices-0.17.0.tgz", - "integrity": "sha512-A7RXuKQvdqSWOpn7ZVQo4S33O0vCfPBnUSf7FwE0zNCasqwZVUaCXePuWo5HBpWw68KJcwObZDHpFk6HKH6MYQ==", - "license": "MIT", - "dependencies": { - "graphology-utils": "^2.4.2", - "mnemonist": "^0.39.0" - }, - "peerDependencies": { - "graphology-types": ">=0.20.0" - } - }, - "node_modules/graphology-layout": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/graphology-layout/-/graphology-layout-0.6.1.tgz", - "integrity": "sha512-m9aMvbd0uDPffUCFPng5ibRkb2pmfNvdKjQWeZrf71RS1aOoat5874+DcyNfMeCT4aQguKC7Lj9eCbqZj/h8Ag==", - "license": "MIT", - "dependencies": { - "graphology-utils": "^2.3.0", - "pandemonium": "^2.4.0" - }, - "peerDependencies": { - "graphology-types": ">=0.19.0" - } - }, - "node_modules/graphology-layout-forceatlas2": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/graphology-layout-forceatlas2/-/graphology-layout-forceatlas2-0.10.1.tgz", - "integrity": "sha512-ogzBeF1FvWzjkikrIFwxhlZXvD2+wlY54lqhsrWprcdPjopM2J9HoMweUmIgwaTvY4bUYVimpSsOdvDv1gPRFQ==", - "license": "MIT", - "dependencies": { - "graphology-utils": "^2.1.0" - }, - "peerDependencies": { - "graphology-types": ">=0.19.0" - } - }, - "node_modules/graphology-layout-noverlap": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/graphology-layout-noverlap/-/graphology-layout-noverlap-0.4.2.tgz", - "integrity": "sha512-13WwZSx96zim6l1dfZONcqLh3oqyRcjIBsqz2c2iJ3ohgs3605IDWjldH41Gnhh462xGB1j6VGmuGhZ2FKISXA==", - "license": "MIT", - "dependencies": { - "graphology-utils": "^2.3.0" - }, - "peerDependencies": { - "graphology-types": ">=0.19.0" - } - }, - "node_modules/graphology-metrics": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/graphology-metrics/-/graphology-metrics-2.3.1.tgz", - "integrity": "sha512-131GRSKUR8DrGkLZSYKM3cwxEg+jqXvv1yLh/KgO0My7BOiuo80r0Qrsnv2N3ZjcOlh8namUS4sSk+cCVnTgKA==", - "license": "MIT", - "dependencies": { - "graphology-indices": "^0.17.0", - "graphology-shortest-path": "^2.0.0", - "graphology-utils": "^2.4.4", - "mnemonist": "^0.39.0" - }, - "peerDependencies": { - "graphology-types": ">=0.20.0" - } - }, - "node_modules/graphology-shortest-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/graphology-shortest-path/-/graphology-shortest-path-2.1.0.tgz", - "integrity": "sha512-KbT9CTkP/u72vGEJzyRr24xFC7usI9Es3LMmCPHGwQ1KTsoZjxwA9lMKxfU0syvT/w+7fZUdB/Hu2wWYcJBm6Q==", - "license": "MIT", - "dependencies": { - "@yomguithereal/helpers": "^1.1.1", - "graphology-indices": "^0.17.0", - "graphology-utils": "^2.4.3", - "mnemonist": "^0.39.0" - }, - "peerDependencies": { - "graphology-types": ">=0.20.0" - } - }, - "node_modules/graphology-types": { - "version": "0.24.7", - "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.7.tgz", - "integrity": "sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==", - "license": "MIT", - "peer": true - }, - "node_modules/graphology-utils": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz", - "integrity": "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==", - "license": "MIT", - "peerDependencies": { - "graphology-types": ">=0.23.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -6573,12 +5974,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, - "node_modules/hold-event": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/hold-event/-/hold-event-0.2.0.tgz", - "integrity": "sha512-rko5P1XgHzy4B0NR0xVHEpWPgj0i23f8Mf8qsOugd1CHvfLR0PyIyy+8TAQQA9v8qAa1OZ4XuCKk04rxmPGHNQ==", - "license": "MIT" - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6690,15 +6085,6 @@ "node": ">= 0.4" } }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/intl-messageformat": { "version": "10.7.7", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.7.tgz", @@ -7226,27 +6612,6 @@ "node": ">= 0.4" } }, - "node_modules/its-fine": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.2.5.tgz", - "integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==", - "license": "MIT", - "dependencies": { - "@types/react-reconciler": "^0.28.0" - }, - "peerDependencies": { - "react": ">=18.0" - } - }, - "node_modules/its-fine/node_modules/@types/react-reconciler": { - "version": "0.28.8", - "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.8.tgz", - "integrity": "sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -8114,16 +7479,6 @@ "yallist": "^3.0.2" } }, - "node_modules/maath": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz", - "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", - "license": "MIT", - "peerDependencies": { - "@types/three": ">=0.134.0", - "three": ">=0.134.0" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8180,21 +7535,6 @@ "node": ">= 8" } }, - "node_modules/meshline": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", - "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==", - "license": "MIT", - "peerDependencies": { - "three": ">=0.137" - } - }, - "node_modules/meshoptimizer": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", - "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", - "license": "MIT" - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -8266,15 +7606,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mnemonist": { - "version": "0.39.8", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", - "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", - "license": "MIT", - "dependencies": { - "obliterator": "^2.0.1" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -8477,12 +7808,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", - "license": "MIT" - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8573,15 +7898,6 @@ "dev": true, "license": "BlueOak-1.0.0" }, - "node_modules/pandemonium": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/pandemonium/-/pandemonium-2.4.1.tgz", - "integrity": "sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g==", - "license": "MIT", - "dependencies": { - "mnemonist": "^0.39.2" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8990,12 +8306,6 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, - "node_modules/potpack": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", - "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", - "license": "ISC" - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -9129,18 +8439,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-composer": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz", - "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.6.0" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -9222,41 +8520,6 @@ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-merge-refs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz", - "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/react-reconciler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz", - "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.21.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^18.0.0" - } - }, - "node_modules/react-reconciler/node_modules/scheduler": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", - "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -9368,19 +8631,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/react-use-measure": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", - "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", - "license": "MIT", - "dependencies": { - "debounce": "^1.2.1" - }, - "peerDependencies": { - "react": ">=16.13", - "react-dom": ">=16.13" - } - }, "node_modules/react-zoom-pan-pinch": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.6.1.tgz", @@ -9436,86 +8686,6 @@ "node": ">=8.10.0" } }, - "node_modules/reagraph": { - "version": "4.20.1", - "resolved": "https://registry.npmjs.org/reagraph/-/reagraph-4.20.1.tgz", - "integrity": "sha512-1STKs/5pZrIDA3yC7SVbl+vzHvNqxWPVUa/6E2GABn93oVpoFMezk9SyZ3W7KnJzKhfSxFDf+C5frjvU+/wCoA==", - "license": "Apache-2.0", - "dependencies": { - "@react-spring/three": "9.6.1", - "@react-three/fiber": "8.13.5", - "@use-gesture/react": "^10.3.1", - "camera-controls": "^2.8.3", - "classnames": "^2.5.1", - "d3-array": "^3.2.4", - "d3-force-3d": "^3.0.3", - "d3-hierarchy": "^3.1.2", - "d3-scale": "^4.0.2", - "ellipsize": "^0.5.1", - "glodrei": "^0.0.1", - "graphology": "^0.25.4", - "graphology-layout": "^0.6.1", - "graphology-layout-forceatlas2": "^0.10.1", - "graphology-layout-noverlap": "^0.4.2", - "graphology-metrics": "^2.1.0", - "graphology-shortest-path": "^2.0.2", - "hold-event": "^0.2.0", - "reakeys": "^2.0.3", - "three": "^0.154.0", - "three-stdlib": "^2.23.13", - "zustand": "4.3.9" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/reagraph/node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/reagraph/node_modules/zustand": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.9.tgz", - "integrity": "sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw==", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "1.2.0" - }, - "engines": { - "node": ">=12.7.0" - }, - "peerDependencies": { - "immer": ">=9.0", - "react": ">=16.8" - }, - "peerDependenciesMeta": { - "immer": { - "optional": true - }, - "react": { - "optional": true - } - } - }, - "node_modules/reakeys": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/reakeys/-/reakeys-2.0.3.tgz", - "integrity": "sha512-5qeGH9xtvFITi+9AyPeTmPhzjDTEBRZICxAg6RJFuEgWFKMHqr6mnMIaL9fgOKJMBzLWCBorpUhyiB824f0EyA==", - "license": "Apache-2.0", - "dependencies": { - "ctrl-keys": "^1.0.3" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -9573,15 +8743,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -9944,32 +9105,6 @@ "node": ">=8" } }, - "node_modules/stats-gl": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz", - "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==", - "license": "MIT", - "dependencies": { - "@types/three": "*", - "three": "^0.170.0" - }, - "peerDependencies": { - "@types/three": "*", - "three": "*" - } - }, - "node_modules/stats-gl/node_modules/three": { - "version": "0.170.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz", - "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", - "license": "MIT" - }, - "node_modules/stats.js": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", - "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", - "license": "MIT" - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10318,15 +9453,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/suspend-react": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", - "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", - "license": "MIT", - "peerDependencies": { - "react": ">=17.0" - } - }, "node_modules/tailwindcss": { "version": "3.4.15", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", @@ -10445,44 +9571,6 @@ "node": ">=0.8" } }, - "node_modules/three": { - "version": "0.154.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.154.0.tgz", - "integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==", - "license": "MIT" - }, - "node_modules/three-mesh-bvh": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.6.tgz", - "integrity": "sha512-rCjsnxEqR9r1/C/lCqzGLS67NDty/S/eT6rAJfDvsanrIctTWdNoR4ZOGWewCB13h1QkVo2BpmC0wakj1+0m8A==", - "license": "MIT", - "peerDependencies": { - "three": ">= 0.151.0" - } - }, - "node_modules/three-stdlib": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.34.0.tgz", - "integrity": "sha512-U5qJYWgUKBFJqr1coMSbczA964uvouzBjQbtJlaI9LfMwy7hr+kc1Mfh0gqi/2872KmGu9utgff6lj8Oti8+VQ==", - "license": "MIT", - "dependencies": { - "@types/draco3d": "^1.4.0", - "@types/offscreencanvas": "^2019.6.4", - "@types/webxr": "^0.5.2", - "draco3d": "^1.4.1", - "fflate": "^0.6.9", - "potpack": "^1.0.1" - }, - "peerDependencies": { - "three": ">=0.128.0" - } - }, - "node_modules/three-stdlib/node_modules/fflate": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", - "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", - "license": "MIT" - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -10503,36 +9591,6 @@ "node": ">=8.0" } }, - "node_modules/troika-three-text": { - "version": "0.47.2", - "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.2.tgz", - "integrity": "sha512-qylT0F+U7xGs+/PEf3ujBdJMYWbn0Qci0kLqI5BJG2kW1wdg4T1XSxneypnF05DxFqJhEzuaOR9S2SjiyknMng==", - "license": "MIT", - "dependencies": { - "bidi-js": "^1.0.2", - "troika-three-utils": "^0.47.2", - "troika-worker-utils": "^0.47.2", - "webgl-sdf-generator": "1.1.1" - }, - "peerDependencies": { - "three": ">=0.125.0" - } - }, - "node_modules/troika-three-utils": { - "version": "0.47.2", - "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.2.tgz", - "integrity": "sha512-/28plhCxfKtH7MSxEGx8e3b/OXU5A0xlwl+Sbdp0H8FXUHKZDoksduEKmjQayXYtxAyuUiCRunYIv/8Vi7aiyg==", - "license": "MIT", - "peerDependencies": { - "three": ">=0.125.0" - } - }, - "node_modules/troika-worker-utils": { - "version": "0.47.2", - "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.2.tgz", - "integrity": "sha512-mzss4MeyzUkYBppn4x5cdAqrhBHFEuVmMMgLMTyFV23x6GvQMyo+/R5E5Lsbrt7WSt5RfvewjcwD1DChRTA9lA==", - "license": "MIT" - }, "node_modules/ts-api-utils": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", @@ -10608,15 +9666,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tunnel-rat": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", - "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", - "license": "MIT", - "dependencies": { - "zustand": "^4.3.2" - } - }, "node_modules/turbo-stream": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", @@ -10881,28 +9930,6 @@ "dev": true, "license": "MIT" }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -10994,17 +10021,6 @@ "makeerror": "1.0.12" } }, - "node_modules/webgl-constants": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", - "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" - }, - "node_modules/webgl-sdf-generator": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", - "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", - "license": "MIT" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/rsconcept/frontend/package.json b/rsconcept/frontend/package.json index 3dcbf258..001b7300 100644 --- a/rsconcept/frontend/package.json +++ b/rsconcept/frontend/package.json @@ -36,7 +36,6 @@ "react-tooltip": "^5.28.0", "react-zoom-pan-pinch": "^3.6.1", "reactflow": "^11.11.4", - "reagraph": "^4.20.1", "use-debounce": "^10.0.4" }, "devDependencies": { diff --git a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx index 50f729fa..1c24a51d 100644 --- a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx +++ b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx @@ -139,6 +139,7 @@ function PickMultiConstituenta({ graph={foldedGraph} isCore={cstID => isBasicConcept(schema.cstByID.get(cstID)?.cst_type)} isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited} + selected={selected} setSelected={setSelected} emptySelection={selected.length === 0} className='w-fit' diff --git a/rsconcept/frontend/src/components/select/ToolbarGraphSelection.tsx b/rsconcept/frontend/src/components/select/ToolbarGraphSelection.tsx index 6258a384..8cf72033 100644 --- a/rsconcept/frontend/src/components/select/ToolbarGraphSelection.tsx +++ b/rsconcept/frontend/src/components/select/ToolbarGraphSelection.tsx @@ -19,15 +19,17 @@ import MiniButton from '../ui/MiniButton'; interface ToolbarGraphSelectionProps extends CProps.Styling { graph: Graph; + selected: number[]; isCore: (item: number) => boolean; - isOwned: (item: number) => boolean; - setSelected: React.Dispatch>; + isOwned?: (item: number) => boolean; + setSelected: (newSelection: number[]) => void; emptySelection?: boolean; } function ToolbarGraphSelection({ className, graph, + selected, isCore, isOwned, setSelected, @@ -40,13 +42,13 @@ function ToolbarGraphSelection({ }, [setSelected, graph, isCore]); const handleSelectOwned = useCallback( - () => setSelected([...graph.nodes.keys()].filter(isOwned)), + () => (isOwned ? setSelected([...graph.nodes.keys()].filter(isOwned)) : undefined), [setSelected, graph, isOwned] ); const handleInvertSelection = useCallback( - () => setSelected(prev => [...graph.nodes.keys()].filter(item => !prev.includes(item))), - [setSelected, graph] + () => setSelected([...graph.nodes.keys()].filter(item => !selected.includes(item))), + [setSelected, selected, graph] ); return ( @@ -60,31 +62,31 @@ function ToolbarGraphSelection({ } - onClick={() => setSelected(prev => [...prev, ...graph.expandAllInputs(prev)])} + onClick={() => setSelected([...selected, ...graph.expandAllInputs(selected)])} disabled={emptySelection} /> } - onClick={() => setSelected(prev => [...prev, ...graph.expandAllOutputs(prev)])} + onClick={() => setSelected([...selected, ...graph.expandAllOutputs(selected)])} disabled={emptySelection} /> } - onClick={() => setSelected(prev => graph.maximizePart(prev))} + onClick={() => setSelected(graph.maximizePart(selected))} disabled={emptySelection} /> } - onClick={() => setSelected(prev => [...prev, ...graph.expandInputs(prev)])} + onClick={() => setSelected([...selected, ...graph.expandInputs(selected)])} disabled={emptySelection} /> } - onClick={() => setSelected(prev => [...prev, ...graph.expandOutputs(prev)])} + onClick={() => setSelected([...selected, ...graph.expandOutputs(selected)])} disabled={emptySelection} /> } onClick={handleSelectCore} /> - } - onClick={handleSelectOwned} - /> + {isOwned ? ( + } + onClick={handleSelectOwned} + /> + ) : null} ); } diff --git a/rsconcept/frontend/src/components/ui/GraphUI.tsx b/rsconcept/frontend/src/components/ui/GraphUI.tsx deleted file mode 100644 index 0c5a1a62..00000000 --- a/rsconcept/frontend/src/components/ui/GraphUI.tsx +++ /dev/null @@ -1,21 +0,0 @@ -// Reexporting necessary reagraph types. -'use client'; - -import { GraphCanvas as GraphUI } from 'reagraph'; - -export { - type CollapseProps, - type GraphCanvasRef, - type GraphEdge, - type GraphNode, - Sphere, - useSelection -} from 'reagraph'; -export { type LayoutTypes as GraphLayout } from 'reagraph'; - -import { ThreeEvent } from '@react-three/fiber'; - -export type GraphMouseEvent = ThreeEvent; -export type GraphPointerEvent = ThreeEvent; - -export default GraphUI; diff --git a/rsconcept/frontend/src/models/miscellaneous.ts b/rsconcept/frontend/src/models/miscellaneous.ts index 79e3c204..1852cddf 100644 --- a/rsconcept/frontend/src/models/miscellaneous.ts +++ b/rsconcept/frontend/src/models/miscellaneous.ts @@ -57,11 +57,6 @@ export interface MGraphEdgeInternal extends EdgeProps { */ export type GraphColoring = 'none' | 'status' | 'type' | 'schemas'; -/** - * Represents graph node sizing scheme. - */ -export type GraphSizing = 'none' | 'complex' | 'derived'; - /** * Represents manuals topic. */ diff --git a/rsconcept/frontend/src/models/miscellaneousAPI.ts b/rsconcept/frontend/src/models/miscellaneousAPI.ts index 205f0217..ff0e5974 100644 --- a/rsconcept/frontend/src/models/miscellaneousAPI.ts +++ b/rsconcept/frontend/src/models/miscellaneousAPI.ts @@ -3,7 +3,7 @@ */ import { PARAMETER } from '@/utils/constants'; -import { DependencyMode, GraphSizing, Position2D } from './miscellaneous'; +import { DependencyMode, Position2D } from './miscellaneous'; import { IOperationPosition, IOperationSchema, OperationID, OperationType } from './oss'; import { IConstituenta, IRSForm } from './rsform'; @@ -38,19 +38,6 @@ export function applyGraphFilter(target: IRSForm, start: number, mode: Dependenc } } -/** - * Apply {@link GraphSizing} to a given {@link IConstituenta}. - */ -export function applyNodeSizing(target: IConstituenta, sizing: GraphSizing): number | undefined { - if (sizing === 'none') { - return undefined; - } else if (sizing === 'complex') { - return target.is_simple_expression ? 1 : 2; - } else { - return target.spawner ? 1 : 2; - } -} - /** * Calculate insert position for a new {@link IOperation} */ diff --git a/rsconcept/frontend/src/pages/ManualsPage/items/ui/HelpRSGraphTerm.tsx b/rsconcept/frontend/src/pages/ManualsPage/items/ui/HelpRSGraphTerm.tsx index 01354e5f..f8d0df21 100644 --- a/rsconcept/frontend/src/pages/ManualsPage/items/ui/HelpRSGraphTerm.tsx +++ b/rsconcept/frontend/src/pages/ManualsPage/items/ui/HelpRSGraphTerm.tsx @@ -32,8 +32,6 @@ function HelpRSGraphTerm() {

Настройка графа

  • Цвет – покраска узлов
  • -
  • Граф – расположение
  • -
  • Размер – размер узлов
  • Отображение текста
  • @@ -51,7 +49,7 @@ function HelpRSGraphTerm() {

    Изменение узлов

  • Клик на конституенту – выделение
  • - Ctrl + клик – выбор фокус-конституенты + Alt + клик – выбор фокус-конституенты
  • Esc – сбросить выделение diff --git a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx index 174fbcf4..a5c3f1ee 100644 --- a/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx +++ b/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx @@ -32,6 +32,9 @@ import { OssNodeTypes } from './graph/OssNodeTypes'; import NodeContextMenu, { ContextMenuData } from './NodeContextMenu'; import ToolbarOssGraph from './ToolbarOssGraph'; +const ZOOM_MAX = 2; +const ZOOM_MIN = 0.5; + interface OssFlowProps { isModified: boolean; setIsModified: (newValue: boolean) => void; @@ -223,7 +226,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { const imageWidth = PARAMETER.ossImageWidth; const imageHeight = PARAMETER.ossImageHeight; const nodesBounds = getNodesBounds(nodes); - const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, 0.5, 2); + const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, ZOOM_MIN, ZOOM_MAX); toPng(canvas, { backgroundColor: colors.bgDefault, width: imageWidth, @@ -266,7 +269,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { setMenuProps(undefined); }, [controller]); - const handleClickCanvas = useCallback(() => { + const handleCanvasClick = useCallback(() => { handleContextMenuHide(); }, [handleContextMenuHide]); @@ -322,13 +325,13 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { nodesFocusable={false} fitView nodeTypes={OssNodeTypes} - maxZoom={2} - minZoom={0.5} + maxZoom={ZOOM_MAX} + minZoom={ZOOM_MIN} nodesConnectable={false} snapToGrid={true} snapGrid={[PARAMETER.ossGridSize, PARAMETER.ossGridSize]} onNodeContextMenu={handleContextMenu} - onClick={handleClickCanvas} + onClick={handleCanvasClick} > {showGrid ? : null} @@ -338,7 +341,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) { edges, handleNodesChange, handleContextMenu, - handleClickCanvas, + handleCanvasClick, onEdgesChange, handleNodeDoubleClick, showGrid diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx index 5bf44f20..84b71254 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx @@ -1,390 +1,18 @@ -'use client'; +import { ReactFlowProvider } from 'reactflow'; -import clsx from 'clsx'; -import { AnimatePresence } from 'framer-motion'; -import fileDownload from 'js-file-download'; -import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { useDebounce } from 'use-debounce'; +import { ConstituentaID } from '@/models/rsform'; -import InfoConstituenta from '@/components/info/InfoConstituenta'; -import SelectedCounter from '@/components/info/SelectedCounter'; -import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection'; -import { GraphCanvasRef, GraphEdge, GraphLayout, GraphNode } from '@/components/ui/GraphUI'; -import Overlay from '@/components/ui/Overlay'; -import AnimateFade from '@/components/wrap/AnimateFade'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; -import DlgGraphParams from '@/dialogs/DlgGraphParams'; -import useLocalStorage from '@/hooks/useLocalStorage'; -import { GraphColoring, GraphFilterParams, GraphSizing } from '@/models/miscellaneous'; -import { applyNodeSizing } from '@/models/miscellaneousAPI'; -import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform'; -import { isBasicConcept } from '@/models/rsformAPI'; -import { colorBgGraphNode } from '@/styling/color'; -import { PARAMETER, storage } from '@/utils/constants'; -import { convertBase64ToBlob } from '@/utils/utils'; - -import { useRSEdit } from '../RSEditContext'; -import GraphSelectors from './GraphSelectors'; -import TermGraph from './TermGraph'; -import ToolbarFocusedCst from './ToolbarFocusedCst'; -import ToolbarTermGraph from './ToolbarTermGraph'; -import useGraphFilter from './useGraphFilter'; -import ViewHidden from './ViewHidden'; +import TGFlow from './TGFlow'; interface EditorTermGraphProps { onOpenEdit: (cstID: ConstituentaID) => void; } function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { - const controller = useRSEdit(); - const { colors } = useConceptOptions(); - - const [filterParams, setFilterParams] = useLocalStorage(storage.rsgraphFilter, { - noHermits: true, - noTemplates: false, - noTransitive: true, - noText: false, - foldDerived: false, - - focusShowInputs: true, - focusShowOutputs: true, - - allowBase: true, - allowStruct: true, - allowTerm: true, - allowAxiom: true, - allowFunction: true, - allowPredicate: true, - allowConstant: true, - allowTheorem: true - }); - const [showParamsDialog, setShowParamsDialog] = useState(false); - const [focusCst, setFocusCst] = useState(undefined); - const filtered = useGraphFilter(controller.schema, filterParams, focusCst); - - const graphRef = useRef(null); - const [hidden, setHidden] = useState([]); - - const [layout, setLayout] = useLocalStorage(storage.rsgraphLayout, 'treeTd2d'); - const [coloring, setColoring] = useLocalStorage(storage.rsgraphColoring, 'type'); - const [sizing, setSizing] = useLocalStorage(storage.rsgraphSizing, 'derived'); - const [orbit, setOrbit] = useState(false); - const is3D = useMemo(() => layout.includes('3d'), [layout]); - - const [hoverID, setHoverID] = useState(undefined); - const hoverCst = useMemo(() => { - return hoverID && controller.schema?.cstByID.get(hoverID); - }, [controller.schema?.cstByID, hoverID]); - const [hoverCstDebounced] = useDebounce(hoverCst, PARAMETER.graphPopupDelay); - const [hoverLeft, setHoverLeft] = useState(true); - - const [toggleResetView, setToggleResetView] = useState(false); - - useLayoutEffect(() => { - if (!controller.schema) { - return; - } - const newDismissed: ConstituentaID[] = []; - controller.schema.items.forEach(cst => { - if (!filtered.nodes.has(cst.id)) { - newDismissed.push(cst.id); - } - }); - setHidden(newDismissed); - setHoverID(undefined); - }, [controller.schema, filtered]); - - const nodes: GraphNode[] = useMemo(() => { - const result: GraphNode[] = []; - if (!controller.schema) { - return result; - } - filtered.nodes.forEach(node => { - const cst = controller.schema!.cstByID.get(node.id); - if (cst) { - result.push({ - id: String(node.id), - fill: focusCst === cst ? colors.bgPurple : colorBgGraphNode(cst, coloring, colors), - label: `${cst.alias}${cst.is_inherited ? '*' : ''}`, - subLabel: !filterParams.noText ? cst.term_resolved : undefined, - size: applyNodeSizing(cst, sizing) - }); - } - }); - return result; - }, [controller.schema, coloring, sizing, filtered.nodes, filterParams.noText, colors, focusCst]); - - const edges: GraphEdge[] = useMemo(() => { - const result: GraphEdge[] = []; - let edgeID = 1; - filtered.nodes.forEach(source => { - source.outputs.forEach(target => { - if (nodes.find(node => node.id === String(target))) { - result.push({ - id: String(edgeID), - source: String(source.id), - target: String(target) - }); - edgeID += 1; - } - }); - }); - return result; - }, [filtered.nodes, nodes]); - - function handleCreateCst() { - if (!controller.schema) { - return; - } - const definition = controller.selected.map(id => controller.schema!.cstByID.get(id)!.alias).join(' '); - controller.createCst(controller.selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition); - } - - function handleDeleteCst() { - if (!controller.schema || !controller.canDeleteSelected) { - return; - } - controller.promptDeleteCst(); - } - - const handleChangeLayout = useCallback( - (newLayout: GraphLayout) => { - if (newLayout === layout) { - return; - } - setLayout(newLayout); - setTimeout(() => { - setToggleResetView(prev => !prev); - }, PARAMETER.graphRefreshDelay); - }, - [layout, setLayout] - ); - - const handleChangeParams = useCallback( - (params: GraphFilterParams) => { - setFilterParams(params); - }, - [setFilterParams] - ); - - const handleSaveImage = useCallback(() => { - if (!graphRef?.current) { - return; - } - const data = graphRef.current.exportCanvas(); - try { - fileDownload(convertBase64ToBlob(data), 'graph.png', 'data:image/png;base64'); - } catch (error) { - console.error(error); - } - }, [graphRef]); - - function handleKeyDown(event: React.KeyboardEvent) { - if (controller.isProcessing) { - return; - } - if (event.key === 'Escape') { - event.preventDefault(); - event.stopPropagation(); - setFocusCst(undefined); - controller.deselectAll(); - return; - } - if (!controller.isContentEditable) { - return; - } - if (event.key === 'Delete') { - event.preventDefault(); - event.stopPropagation(); - handleDeleteCst(); - return; - } - } - - const handleFoldDerived = useCallback(() => { - setFilterParams(prev => ({ - ...prev, - foldDerived: !prev.foldDerived - })); - setTimeout(() => { - setToggleResetView(prev => !prev); - }, PARAMETER.graphRefreshDelay); - }, [setFilterParams, setToggleResetView]); - - const handleSetFocus = useCallback( - (cstID: ConstituentaID | undefined) => { - const target = cstID !== undefined ? controller.schema?.cstByID.get(cstID) : cstID; - setFocusCst(prev => (prev === target ? undefined : target)); - if (target) { - controller.setSelected([]); - } - }, - [controller] - ); - - const graph = useMemo( - () => ( - - ), - [ - graphRef, - edges, - nodes, - controller.selected, - layout, - is3D, - orbit, - setHoverID, - onOpenEdit, - toggleResetView, - controller.select, - controller.deselect, - handleSetFocus - ] - ); - - const selectors = useMemo( - () => ( - - ), - [coloring, controller.schema, layout, sizing, handleChangeLayout, setColoring, setSizing] - ); - const viewHidden = useMemo( - () => ( - - ), - [hidden, controller.selected, controller.schema, coloring, controller.toggleSelect, handleSetFocus, onOpenEdit] - ); - return ( - <> - - {showParamsDialog ? ( - setShowParamsDialog(false)} - initial={filterParams} - onConfirm={handleChangeParams} - /> - ) : null} - - - - setShowParamsDialog(true)} - onCreate={handleCreateCst} - onDelete={handleDeleteCst} - onFitView={() => setToggleResetView(prev => !prev)} - onSaveImage={handleSaveImage} - toggleOrbit={() => setOrbit(prev => !prev)} - toggleFoldDerived={handleFoldDerived} - toggleNoText={() => - setFilterParams(prev => ({ - ...prev, - noText: !prev.noText - })) - } - /> - {!focusCst ? ( - isBasicConcept(controller.schema?.cstByID.get(cstID)?.cst_type)} - isOwned={cstID => !controller.schema?.cstByID.get(cstID)?.is_inherited} - setSelected={controller.setSelected} - emptySelection={controller.selected.length === 0} - /> - ) : null} - {focusCst ? ( - handleSetFocus(undefined)} - showInputs={filterParams.focusShowInputs} - showOutputs={filterParams.focusShowOutputs} - toggleShowInputs={() => - setFilterParams(prev => ({ - ...prev, - focusShowInputs: !prev.focusShowInputs - })) - } - toggleShowOutputs={() => - setFilterParams(prev => ({ - ...prev, - focusShowOutputs: !prev.focusShowOutputs - })) - } - /> - ) : null} - - - - - - {hoverCst && hoverCstDebounced && hoverCst === hoverCstDebounced ? ( - - - - ) : null} - - -
    - {selectors} - {viewHidden} -
    -
    - - {graph} -
    - + + + ); } diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx index efb6d277..a0d78200 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx @@ -1,57 +1,34 @@ import BadgeHelp from '@/components/info/BadgeHelp'; -import { GraphLayout } from '@/components/ui/GraphUI'; import Overlay from '@/components/ui/Overlay'; import SelectSingle from '@/components/ui/SelectSingle'; -import { GraphColoring, GraphSizing, HelpTopic } from '@/models/miscellaneous'; +import { GraphColoring, HelpTopic } from '@/models/miscellaneous'; import { IRSForm } from '@/models/rsform'; -import { mapLabelColoring, mapLabelLayout, mapLabelSizing } from '@/utils/labels'; -import { SelectorGraphColoring, SelectorGraphLayout, SelectorGraphSizing } from '@/utils/selectors'; +import { mapLabelColoring } from '@/utils/labels'; +import { SelectorGraphColoring } from '@/utils/selectors'; import SchemasGuide from './SchemasGuide'; interface GraphSelectorsProps { schema?: IRSForm; coloring: GraphColoring; - layout: GraphLayout; - sizing: GraphSizing; - - setLayout: (newValue: GraphLayout) => void; - setColoring: (newValue: GraphColoring) => void; - setSizing: (newValue: GraphSizing) => void; + onChangeColoring: (newValue: GraphColoring) => void; } -function GraphSelectors({ schema, coloring, setColoring, layout, setLayout, sizing, setSizing }: GraphSelectorsProps) { +function GraphSelectors({ schema, coloring, onChangeColoring }: GraphSelectorsProps) { return (
    - setLayout(data?.value ?? SelectorGraphLayout[0].value)} - /> - + {coloring === 'status' ? : null} {coloring === 'type' ? : null} {coloring === 'schemas' && !!schema ? : null} setColoring(data?.value ?? SelectorGraphColoring[0].value)} - /> - setSizing(data?.value ?? SelectorGraphSizing[0].value)} + onChange={data => onChangeColoring(data?.value ?? SelectorGraphColoring[0].value)} />
    ); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx new file mode 100644 index 00000000..ff81e8ec --- /dev/null +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TGFlow.tsx @@ -0,0 +1,480 @@ +'use client'; + +import clsx from 'clsx'; +import { AnimatePresence } from 'framer-motion'; +import { toPng } from 'html-to-image'; +import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { toast } from 'react-toastify'; +import { + Edge, + getNodesBounds, + getViewportForBounds, + MarkerType, + Node, + ReactFlow, + useEdgesState, + useNodesState, + useOnSelectionChange, + useReactFlow +} from 'reactflow'; +import { useStoreApi } from 'reactflow'; +import { useDebounce } from 'use-debounce'; + +import InfoConstituenta from '@/components/info/InfoConstituenta'; +import SelectedCounter from '@/components/info/SelectedCounter'; +import { CProps } from '@/components/props'; +import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection'; +import Overlay from '@/components/ui/Overlay'; +import AnimateFade from '@/components/wrap/AnimateFade'; +import { useConceptOptions } from '@/context/ConceptOptionsContext'; +import DlgGraphParams from '@/dialogs/DlgGraphParams'; +import useLocalStorage from '@/hooks/useLocalStorage'; +import { GraphColoring, GraphFilterParams } from '@/models/miscellaneous'; +import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform'; +import { isBasicConcept } from '@/models/rsformAPI'; +import { colorBgGraphNode } from '@/styling/color'; +import { PARAMETER, storage } from '@/utils/constants'; +import { errors } from '@/utils/labels'; + +import { useRSEdit } from '../RSEditContext'; +import { TGEdgeTypes } from './graph/TGEdgeTypes'; +import { applyLayout } from './graph/TGLayout'; +import { TGNodeData } from './graph/TGNode'; +import { TGNodeTypes } from './graph/TGNodeTypes'; +import GraphSelectors from './GraphSelectors'; +import ToolbarFocusedCst from './ToolbarFocusedCst'; +import ToolbarTermGraph from './ToolbarTermGraph'; +import useGraphFilter from './useGraphFilter'; +import ViewHidden from './ViewHidden'; + +const ZOOM_MAX = 3; +const ZOOM_MIN = 0.25; + +interface TGFlowProps { + onOpenEdit: (cstID: ConstituentaID) => void; +} + +function TGFlow({ onOpenEdit }: TGFlowProps) { + const { colors, mainHeight } = useConceptOptions(); + const controller = useRSEdit(); + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges] = useEdgesState([]); + const flow = useReactFlow(); + const store = useStoreApi(); + + const [filterParams, setFilterParams] = useLocalStorage(storage.rsgraphFilter, { + noHermits: true, + noTemplates: false, + noTransitive: true, + noText: false, + foldDerived: false, + + focusShowInputs: true, + focusShowOutputs: true, + + allowBase: true, + allowStruct: true, + allowTerm: true, + allowAxiom: true, + allowFunction: true, + allowPredicate: true, + allowConstant: true, + allowTheorem: true + }); + const [showParamsDialog, setShowParamsDialog] = useState(false); + const [focusCst, setFocusCst] = useState(undefined); + const filteredGraph = useGraphFilter(controller.schema, filterParams, focusCst); + + const [hidden, setHidden] = useState([]); + + const [coloring, setColoring] = useLocalStorage(storage.rsgraphColoring, 'type'); + + const [isDragging, setIsDragging] = useState(false); + + const [hoverID, setHoverID] = useState(undefined); + const hoverCst = useMemo(() => { + return hoverID && controller.schema?.cstByID.get(hoverID); + }, [controller.schema?.cstByID, hoverID]); + const [hoverCstDebounced] = useDebounce(hoverCst, PARAMETER.graphPopupDelay); + const [hoverLeft, setHoverLeft] = useState(true); + + const [toggleResetView, setToggleResetView] = useState(false); + + const { addSelectedNodes } = store.getState(); + + const onSelectionChange = useCallback( + ({ nodes }: { nodes: Node[] }) => { + const ids = nodes.map(node => Number(node.id)); + if (ids.length === 0) { + controller.setSelected([]); + } else { + controller.setSelected(prev => [...prev.filter(nodeID => !filteredGraph.hasNode(nodeID)), ...ids]); + } + }, + [controller, filteredGraph] + ); + + useOnSelectionChange({ + onChange: onSelectionChange + }); + + useLayoutEffect(() => { + if (!controller.schema) { + return; + } + const newDismissed: ConstituentaID[] = []; + controller.schema.items.forEach(cst => { + if (!filteredGraph.nodes.has(cst.id)) { + newDismissed.push(cst.id); + } + }); + setHidden(newDismissed); + setHoverID(undefined); + }, [controller.schema, filteredGraph]); + + useLayoutEffect(() => { + if (!controller.schema) { + return; + } + const newNodes: Node[] = []; + filteredGraph.nodes.forEach(node => { + const cst = controller.schema!.cstByID.get(node.id); + if (cst) { + newNodes.push({ + id: String(node.id), + type: 'concept', + selected: controller.selected.includes(node.id), + position: { x: 0, y: 0 }, + data: { + fill: focusCst === cst ? colors.bgPurple : colorBgGraphNode(cst, coloring, colors), + label: cst.alias, + subLabel: !filterParams.noText ? cst.term_resolved : '' + } + }); + } + }); + + const newEdges: Edge[] = []; + let edgeID = 1; + filteredGraph.nodes.forEach(source => { + source.outputs.forEach(target => { + if (newNodes.find(node => node.id === String(target))) { + newEdges.push({ + id: String(edgeID), + source: String(source.id), + target: String(target), + type: 'termEdge', + focusable: false, + markerEnd: { + type: MarkerType.ArrowClosed, + width: 20, + height: 20 + } + }); + edgeID += 1; + } + }); + }); + + applyLayout(newNodes, newEdges, !filterParams.noText); + + setNodes(newNodes); + setEdges(newEdges); + // NOTE: Do not rerender on controller.selected change because it is only needed during first load + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filteredGraph, setNodes, setEdges, controller.schema, filterParams.noText, focusCst, coloring, colors, flow]); + + useLayoutEffect(() => { + setTimeout(() => { + flow.fitView({ duration: PARAMETER.zoomDuration }); + }, PARAMETER.minimalTimeout); + }, [toggleResetView, flow, focusCst, filterParams]); + + function handleSetSelected(newSelection: number[]) { + controller.setSelected(newSelection); + addSelectedNodes(newSelection.map(id => String(id))); + } + + function handleCreateCst() { + if (!controller.schema) { + return; + } + const definition = controller.selected.map(id => controller.schema!.cstByID.get(id)!.alias).join(' '); + controller.createCst(controller.selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition); + } + + function handleDeleteCst() { + if (!controller.schema || !controller.canDeleteSelected) { + return; + } + controller.promptDeleteCst(); + } + + const handleChangeParams = useCallback( + (params: GraphFilterParams) => { + setFilterParams(params); + }, + [setFilterParams] + ); + + const handleSaveImage = useCallback(() => { + if (!controller.schema) { + return; + } + const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport'); + if (canvas === null) { + toast.error(errors.imageFailed); + return; + } + + const imageWidth = PARAMETER.ossImageWidth; + const imageHeight = PARAMETER.ossImageHeight; + const nodesBounds = getNodesBounds(nodes); + const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, ZOOM_MIN, ZOOM_MAX); + toPng(canvas, { + backgroundColor: colors.bgDefault, + width: imageWidth, + height: imageHeight, + style: { + width: String(imageWidth), + height: String(imageHeight), + transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom * 2})` + } + }) + .then(dataURL => { + const a = document.createElement('a'); + a.setAttribute('download', `${controller.schema?.alias ?? 'graph'}.png`); + a.setAttribute('href', dataURL); + a.click(); + }) + .catch(error => { + console.error(error); + toast.error(errors.imageFailed); + }); + }, [colors, nodes, controller.schema]); + + function handleKeyDown(event: React.KeyboardEvent) { + if (controller.isProcessing) { + return; + } + if (event.key === 'Escape') { + event.preventDefault(); + event.stopPropagation(); + setFocusCst(undefined); + handleSetSelected([]); + return; + } + if (!controller.isContentEditable) { + return; + } + if (event.key === 'Delete') { + event.preventDefault(); + event.stopPropagation(); + handleDeleteCst(); + return; + } + } + + const handleFoldDerived = useCallback(() => { + setFilterParams(prev => ({ + ...prev, + foldDerived: !prev.foldDerived + })); + setTimeout(() => { + setToggleResetView(prev => !prev); + }, PARAMETER.graphRefreshDelay); + }, [setFilterParams, setToggleResetView]); + + const handleSetFocus = useCallback( + (cstID: ConstituentaID | undefined) => { + const target = cstID !== undefined ? controller.schema?.cstByID.get(cstID) : cstID; + setFocusCst(prev => (prev === target ? undefined : target)); + if (target) { + controller.setSelected([]); + } + }, + [controller] + ); + + const handleNodeClick = useCallback( + (event: CProps.EventMouse, cstID: ConstituentaID) => { + if (event.altKey) { + handleSetFocus(cstID); + } + }, + [handleSetFocus] + ); + + const handleNodeDoubleClick = useCallback( + (event: CProps.EventMouse, cstID: ConstituentaID) => { + event.preventDefault(); + event.stopPropagation(); + onOpenEdit(cstID); + }, + [onOpenEdit] + ); + + const handleNodeEnter = useCallback( + (event: CProps.EventMouse, cstID: ConstituentaID) => { + setHoverID(cstID); + setHoverLeft( + event.clientX / window.innerWidth >= PARAMETER.graphHoverXLimit || + event.clientY / window.innerHeight >= PARAMETER.graphHoverYLimit + ); + }, + [setHoverID, setHoverLeft] + ); + + const handleNodeLeave = useCallback(() => { + setHoverID(undefined); + }, [setHoverID]); + + const selectors = useMemo( + () => , + [coloring, controller.schema, setColoring] + ); + const viewHidden = useMemo( + () => ( + + ), + [hidden, controller.selected, controller.schema, coloring, controller.toggleSelect, handleSetFocus, onOpenEdit] + ); + + const graph = useMemo( + () => ( +
    + setIsDragging(true)} + onNodeDragStop={() => setIsDragging(false)} + onNodeMouseEnter={(event, node) => handleNodeEnter(event, Number(node.id))} + onNodeMouseLeave={handleNodeLeave} + onNodeClick={(event, node) => handleNodeClick(event, Number(node.id))} + onNodeDoubleClick={(event, node) => handleNodeDoubleClick(event, Number(node.id))} + /> +
    + ), + [nodes, edges, mainHeight, handleNodeClick, handleNodeDoubleClick, handleNodeLeave, handleNodeEnter, onNodesChange] + ); + + return ( + <> + + {showParamsDialog ? ( + setShowParamsDialog(false)} + initial={filterParams} + onConfirm={handleChangeParams} + /> + ) : null} + + + + + setShowParamsDialog(true)} + onCreate={handleCreateCst} + onDelete={handleDeleteCst} + onFitView={() => setToggleResetView(prev => !prev)} + onSaveImage={handleSaveImage} + toggleFoldDerived={handleFoldDerived} + toggleNoText={() => + setFilterParams(prev => ({ + ...prev, + noText: !prev.noText + })) + } + /> + {!focusCst ? ( + isBasicConcept(controller.schema?.cstByID.get(cstID)?.cst_type)} + isOwned={ + controller.schema && controller.schema.inheritance.length > 0 + ? cstID => !controller.schema!.cstByID.get(cstID)?.is_inherited + : undefined + } + selected={controller.selected} + setSelected={handleSetSelected} + emptySelection={controller.selected.length === 0} + /> + ) : null} + {focusCst ? ( + handleSetFocus(undefined)} + showInputs={filterParams.focusShowInputs} + showOutputs={filterParams.focusShowOutputs} + toggleShowInputs={() => + setFilterParams(prev => ({ + ...prev, + focusShowInputs: !prev.focusShowInputs + })) + } + toggleShowOutputs={() => + setFilterParams(prev => ({ + ...prev, + focusShowOutputs: !prev.focusShowOutputs + })) + } + /> + ) : null} + + + + + {!isDragging && hoverCst && hoverCstDebounced && hoverCst === hoverCstDebounced ? ( + + + + ) : null} + + +
    + {selectors} + {viewHidden} +
    +
    + {graph} +
    + + ); +} + +export default TGFlow; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx deleted file mode 100644 index 14178fda..00000000 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx +++ /dev/null @@ -1,137 +0,0 @@ -'use client'; - -import { RefObject, useCallback, useLayoutEffect } from 'react'; - -import GraphUI, { - CollapseProps, - GraphCanvasRef, - GraphEdge, - GraphLayout, - GraphMouseEvent, - GraphNode, - GraphPointerEvent, - useSelection -} from '@/components/ui/GraphUI'; -import { useConceptOptions } from '@/context/ConceptOptionsContext'; -import { ConstituentaID } from '@/models/rsform'; -import { graphDarkT, graphLightT } from '@/styling/color'; -import { PARAMETER, resources } from '@/utils/constants'; - -interface TermGraphProps { - graphRef: RefObject; - nodes: GraphNode[]; - edges: GraphEdge[]; - selectedIDs: ConstituentaID[]; - - layout: GraphLayout; - is3D: boolean; - orbit: boolean; - - setHoverID: (newID: ConstituentaID | undefined) => void; - setHoverLeft: (value: boolean) => void; - onEdit: (cstID: ConstituentaID) => void; - onSelectCentral: (selectedID: ConstituentaID) => void; - onSelect: (newID: ConstituentaID) => void; - onDeselect: (newID: ConstituentaID) => void; - - toggleResetView: boolean; -} - -function TermGraph({ - graphRef, - nodes, - edges, - selectedIDs, - layout, - is3D, - orbit, - toggleResetView, - setHoverID, - setHoverLeft, - onEdit, - onSelectCentral, - onSelect, - onDeselect -}: TermGraphProps) { - const { mainHeight, darkMode } = useConceptOptions(); - - const { selections, setSelections } = useSelection({ - ref: graphRef, - nodes: nodes, - edges: edges, - type: 'multi', - focusOnSelect: false - }); - - const handleHoverIn = useCallback( - (node: GraphNode, event: GraphPointerEvent) => { - setHoverID(Number(node.id)); - setHoverLeft( - event.clientX / window.innerWidth >= PARAMETER.graphHoverXLimit || - event.clientY / window.innerHeight >= PARAMETER.graphHoverYLimit - ); - }, - [setHoverID, setHoverLeft] - ); - - const handleHoverOut = useCallback(() => { - setHoverID(undefined); - }, [setHoverID]); - - const handleNodeClick = useCallback( - (node: GraphNode, _?: CollapseProps, event?: GraphMouseEvent) => { - if (event?.ctrlKey || event?.metaKey) { - onSelectCentral(Number(node.id)); - } else if (selections.includes(node.id)) { - onDeselect(Number(node.id)); - } else { - onSelect(Number(node.id)); - } - }, - [onSelect, selections, onDeselect, onSelectCentral] - ); - - const handleNodeDoubleClick = useCallback( - (node: GraphNode) => { - onEdit(Number(node.id)); - }, - [onEdit] - ); - - useLayoutEffect(() => { - graphRef.current?.fitNodesInView([], { animated: true }); - }, [toggleResetView, graphRef]); - - useLayoutEffect(() => { - const newSelections = nodes.filter(node => selectedIDs.includes(Number(node.id))).map(node => node.id); - setSelections(newSelections); - }, [selectedIDs, setSelections, nodes]); - - return ( -
    - -
    - ); -} - -export default TermGraph; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx index 1e99ad57..42d167c9 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ToolbarTermGraph.tsx @@ -8,7 +8,6 @@ import { IconFitImage, IconImage, IconNewItem, - IconRotate3D, IconText, IconTextOff, IconTypeGraph @@ -22,9 +21,6 @@ import { PARAMETER } from '@/utils/constants'; import { useRSEdit } from '../RSEditContext'; interface ToolbarTermGraphProps { - is3D: boolean; - - orbit: boolean; noText: boolean; foldDerived: boolean; @@ -36,17 +32,13 @@ interface ToolbarTermGraphProps { toggleFoldDerived: () => void; toggleNoText: () => void; - toggleOrbit: () => void; } function ToolbarTermGraph({ - is3D, noText, foldDerived, toggleNoText, toggleFoldDerived, - orbit, - toggleOrbit, showParamsDialog, onCreate, onDelete, @@ -95,12 +87,6 @@ function ToolbarTermGraph({ } onClick={toggleFoldDerived} /> - } - title='Анимация вращения' - disabled={!is3D} - onClick={toggleOrbit} - /> {controller.isContentEditable ? ( + + + ); +} + +export default TGEdge; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdgeTypes.ts b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdgeTypes.ts new file mode 100644 index 00000000..8c037345 --- /dev/null +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGEdgeTypes.ts @@ -0,0 +1,7 @@ +import { EdgeTypes } from 'reactflow'; + +import TGEdge from './TGEdge'; + +export const TGEdgeTypes: EdgeTypes = { + termEdge: TGEdge +}; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGLayout.ts b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGLayout.ts new file mode 100644 index 00000000..44f50999 --- /dev/null +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGLayout.ts @@ -0,0 +1,32 @@ +import dagre from '@dagrejs/dagre'; +import { Edge, Node } from 'reactflow'; + +import { PARAMETER } from '@/utils/constants'; + +import { TGNodeData } from './TGNode'; + +export function applyLayout(nodes: Node[], edges: Edge[], subLabels?: boolean) { + const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); + dagreGraph.setGraph({ + rankdir: 'TB', + ranksep: subLabels ? 60 : 40, + nodesep: subLabels ? 100 : 20, + ranker: 'network-simplex', + align: undefined + }); + nodes.forEach(node => { + dagreGraph.setNode(node.id, { width: 2 * PARAMETER.graphNodeRadius, height: 2 * PARAMETER.graphNodeRadius }); + }); + + edges.forEach(edge => { + dagreGraph.setEdge(edge.source, edge.target); + }); + + dagre.layout(dagreGraph); + + nodes.forEach(node => { + const nodeWithPosition = dagreGraph.node(node.id); + node.position.x = nodeWithPosition.x - PARAMETER.graphNodeRadius; + node.position.y = nodeWithPosition.y - PARAMETER.graphNodeRadius; + }); +} diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNode.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNode.tsx new file mode 100644 index 00000000..4a44a378 --- /dev/null +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNode.tsx @@ -0,0 +1,78 @@ +'use client'; + +import { useMemo } from 'react'; +import { Handle, Position } from 'reactflow'; + +import { useConceptOptions } from '@/context/ConceptOptionsContext'; +import { truncateToLastWord } from '@/utils/utils'; + +const MAX_LABEL_LENGTH = 65; + +export interface TGNodeData { + fill: string; + label: string; + subLabel: string; +} + +/** + * Represents graph AST node internal data. + */ +interface TGNodeInternal { + id: string; + data: TGNodeData; + selected: boolean; + dragging: boolean; + xPos: number; + yPos: number; +} + +function TGNode(node: TGNodeInternal) { + const { colors } = useConceptOptions(); + const subLabel = useMemo(() => truncateToLastWord(node.data.subLabel, MAX_LABEL_LENGTH), [node.data.subLabel]); + + return ( + <> + +
    +
    {node.data.label}
    +
    + {node.data.label} +
    +
    + + {subLabel ? ( +
    15 ? 10 : 12 + }} + > +
    + {subLabel} +
    +
    {subLabel}
    +
    + ) : null} + + ); +} + +export default TGNode; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNodeTypes.ts b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNodeTypes.ts new file mode 100644 index 00000000..852f8ac0 --- /dev/null +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/graph/TGNodeTypes.ts @@ -0,0 +1,7 @@ +import { NodeTypes } from 'reactflow'; + +import TGNode from './TGNode'; + +export const TGNodeTypes: NodeTypes = { + concept: TGNode +}; diff --git a/rsconcept/frontend/src/styling/color.ts b/rsconcept/frontend/src/styling/color.ts index 9f2d550b..6c54749c 100644 --- a/rsconcept/frontend/src/styling/color.ts +++ b/rsconcept/frontend/src/styling/color.ts @@ -20,6 +20,7 @@ export interface IColorTheme { bgDisabled: string; bgPrimary: string; bgSelected: string; + bgActiveSelection: string; bgHover: string; bgWarning: string; @@ -62,6 +63,7 @@ export const lightT: IColorTheme = { bgDisabled: 'var(--cl-bg-60)', bgPrimary: 'var(--cl-prim-bg-100)', bgSelected: 'var(--cl-prim-bg-80)', + bgActiveSelection: 'var(--cl-teal-bg-100)', bgHover: 'var(--cl-prim-bg-60)', bgWarning: 'var(--cl-red-bg-100)', @@ -104,6 +106,7 @@ export const darkT: IColorTheme = { bgDisabled: 'var(--cd-bg-60)', bgPrimary: 'var(--cd-prim-bg-100)', bgSelected: 'var(--cd-prim-bg-80)', + bgActiveSelection: 'var(--cd-teal-bg-100)', bgHover: 'var(--cd-prim-bg-60)', bgWarning: 'var(--cd-red-bg-100)', @@ -184,96 +187,6 @@ export const selectDarkT = { neutral90: darkT.fgWarning }; -/** - * Represents Graph component Light theme. - */ -export const graphLightT = { - canvas: { - background: '#f9fafb' - }, - node: { - fill: '#7ca0ab', - activeFill: '#1DE9AC', - opacity: 1, - selectedOpacity: 1, - inactiveOpacity: 1, - label: { - color: '#2A6475', - stroke: '#fff', - activeColor: '#1DE9AC' - } - }, - lasso: { - border: '1px solid #55aaff', - background: 'rgba(75, 160, 255, 0.1)' - }, - ring: { - fill: '#D8E6EA', - activeFill: '#1DE9AC' - }, - edge: { - fill: '#D8E6EA', - activeFill: '#1DE9AC', - opacity: 1, - selectedOpacity: 1, - inactiveOpacity: 1, - label: { - stroke: '#fff', - color: '#2A6475', - activeColor: '#1DE9AC' - } - }, - arrow: { - fill: '#D8E6EA', - activeFill: '#1DE9AC' - } -}; - -/** - * Represents Graph component Dark theme. - */ -export const graphDarkT = { - canvas: { - background: '#171717' // var(--cd-bg-100) - }, - node: { - fill: '#7a8c9e', - activeFill: '#1DE9AC', - opacity: 1, - selectedOpacity: 1, - inactiveOpacity: 1, - label: { - stroke: '#1E2026', - color: '#ACBAC7', - activeColor: '#1DE9AC' - } - }, - lasso: { - border: '1px solid #55aaff', - background: 'rgba(75, 160, 255, 0.1)' - }, - ring: { - fill: '#54616D', - activeFill: '#1DE9AC' - }, - edge: { - fill: '#474B56', - activeFill: '#1DE9AC', - opacity: 1, - selectedOpacity: 1, - inactiveOpacity: 1, - label: { - stroke: '#1E2026', - color: '#ACBAC7', - activeColor: '#1DE9AC' - } - }, - arrow: { - fill: '#474B56', - activeFill: '#1DE9AC' - } -}; - /** * Represents Brackets highlights Light theme. */ diff --git a/rsconcept/frontend/src/styling/constants.css b/rsconcept/frontend/src/styling/constants.css index b17b59d0..29d35761 100644 --- a/rsconcept/frontend/src/styling/constants.css +++ b/rsconcept/frontend/src/styling/constants.css @@ -38,6 +38,8 @@ --cl-red-fg-100: hsl(000, 072%, 051%); --cl-green-fg-100: hsl(120, 080%, 37%); + --cl-teal-bg-100: hsl(162, 082%, 051%); + /* Dark Theme */ --cd-bg-120: hsl(000, 000%, 005%); --cd-bg-100: hsl(000, 000%, 009%); @@ -59,4 +61,6 @@ --cd-red-bg-100: hsl(000, 100%, 015%); --cd-red-fg-100: hsl(000, 080%, 055%); --cd-green-fg-100: hsl(120, 080%, 042%); + + --cd-teal-bg-100: hsl(162, 082%, 041%); } diff --git a/rsconcept/frontend/src/styling/overrides.css b/rsconcept/frontend/src/styling/overrides.css index fac75bb1..9c5bc669 100644 --- a/rsconcept/frontend/src/styling/overrides.css +++ b/rsconcept/frontend/src/styling/overrides.css @@ -127,10 +127,17 @@ } .react-flow__node-step, -.react-flow__node-token { +.react-flow__node-token, +.react-flow__node-concept { cursor: default; border-radius: 100%; width: 40px; height: 40px; } + +.react-flow__node-concept { + &.selected { + border-color: transparent; + } +} diff --git a/rsconcept/frontend/src/utils/constants.ts b/rsconcept/frontend/src/utils/constants.ts index 3528c798..03561c5b 100644 --- a/rsconcept/frontend/src/utils/constants.ts +++ b/rsconcept/frontend/src/utils/constants.ts @@ -10,8 +10,8 @@ export const PARAMETER = { smallTreeNodes: 50, // amount of nodes threshold for size increase for large graphs refreshTimeout: 100, // milliseconds delay for post-refresh actions minimalTimeout: 10, // milliseconds delay for fast updates - zoomDuration: 500, // milliseconds animation duration + ossImageWidth: 1280, // pixels - size of OSS image ossImageHeight: 960, // pixels - size of OSS image ossContextMenuWidth: 200, // pixels - width of OSS context menu @@ -21,16 +21,16 @@ export const PARAMETER = { ossDistanceX: 180, // pixels - insert x-distance between node centers ossDistanceY: 100, // pixels - insert y-distance between node centers + graphNodeRadius: 20, // pixels - radius of graph node + graphNodePadding: 5, // pixels - padding of graph node graphHoverXLimit: 0.4, // ratio to clientWidth used to determine which side of screen popup should be graphHoverYLimit: 0.6, // ratio to clientHeight used to determine which side of screen popup should be graphPopupDelay: 500, // milliseconds delay for graph popup selections graphRefreshDelay: 10, // milliseconds delay for graph viewpoint reset typificationTruncate: 42, // characters - threshold for long typification - truncate - ossLongLabel: 14, // characters - threshold for long labels - small font ossTruncateLabel: 32, // characters - threshold for long labels - truncate - statSmallThreshold: 3, // characters - threshold for small labels - small font logicLabel: 'LOGIC', @@ -121,9 +121,7 @@ export const storage = { libraryPagination: 'library.pagination', rsgraphFilter: 'rsgraph.filter2', - rsgraphLayout: 'rsgraph.layout', rsgraphColoring: 'rsgraph.coloring', - rsgraphSizing: 'rsgraph.sizing', rsgraphFoldHidden: 'rsgraph.fold_hidden', ossShowGrid: 'oss.show_grid', diff --git a/rsconcept/frontend/src/utils/labels.ts b/rsconcept/frontend/src/utils/labels.ts index 03a9131e..2ccc9c89 100644 --- a/rsconcept/frontend/src/utils/labels.ts +++ b/rsconcept/frontend/src/utils/labels.ts @@ -4,12 +4,11 @@ * Label is a short text used to represent an entity. * Description is a long description used in tooltips. */ -import { GraphLayout } from '@/components/ui/GraphUI'; import { FolderNode } from '@/models/FolderTree'; import { GramData, Grammeme, ReferenceType } from '@/models/language'; import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library'; import { validateLocation } from '@/models/libraryAPI'; -import { CstMatchMode, DependencyMode, GraphColoring, GraphSizing, HelpTopic } from '@/models/miscellaneous'; +import { CstMatchMode, DependencyMode, GraphColoring, HelpTopic } from '@/models/miscellaneous'; import { ISubstitutionErrorDescription, OperationType, SubstitutionErrorType } from '@/models/oss'; import { CstClass, CstType, ExpressionStatus, IConstituenta, IRSForm } from '@/models/rsform'; import { @@ -293,22 +292,6 @@ export function describeLocationHead(head: LocationHead): string { } } -/** - * Retrieves label for graph layout mode. - */ -export const mapLabelLayout = new Map([ - ['treeTd2d', 'Граф: ДеревоВ 2D'], - ['treeTd3d', 'Граф: ДеревоВ 3D'], - ['forceatlas2', 'Граф: Атлас 2D'], - ['forceDirected2d', 'Граф: Силы 2D'], - ['forceDirected3d', 'Граф: Силы 3D'], - ['treeLr2d', 'Граф: ДеревоГ 2D'], - ['treeLr3d', 'Граф: ДеревоГ 3D'], - ['radialOut2d', 'Граф: Радиус 2D'], - ['radialOut3d', 'Граф: Радиус 3D'], - ['circular2d', 'Граф: Круговая'] -]); - /** * Retrieves label for {@link GraphColoring}. */ @@ -319,15 +302,6 @@ export const mapLabelColoring = new Map([ ['schemas', 'Цвет: Схемы'] ]); -/** - * Retrieves label for {@link GraphSizing}. - */ -export const mapLabelSizing = new Map([ - ['none', 'Узлы: Моно'], - ['derived', 'Узлы: Порожденные'], - ['complex', 'Узлы: Простые'] -]); - /** * Retrieves label for {@link ExpressionStatus}. */ diff --git a/rsconcept/frontend/src/utils/selectors.ts b/rsconcept/frontend/src/utils/selectors.ts index ab6a576e..5a399377 100644 --- a/rsconcept/frontend/src/utils/selectors.ts +++ b/rsconcept/frontend/src/utils/selectors.ts @@ -2,32 +2,20 @@ * Module: Mappings for selector UI elements. Do not confuse with html selectors */ -import { GraphLayout } from '@/components/ui/GraphUI'; import { type GramData, Grammeme, ReferenceType } from '@/models/language'; import { grammemeCompare } from '@/models/languageAPI'; -import { GraphColoring, GraphSizing } from '@/models/miscellaneous'; +import { GraphColoring } from '@/models/miscellaneous'; import { CstType } from '@/models/rsform'; -import { labelGrammeme, labelReferenceType, mapLabelColoring, mapLabelLayout, mapLabelSizing } from './labels'; +import { labelGrammeme, labelReferenceType, mapLabelColoring } from './labels'; import { labelCstType } from './labels'; -/** - * Represents options for GraphLayout selector. - */ -export const SelectorGraphLayout: { value: GraphLayout; label: string }[] = // - [...mapLabelLayout.entries()].map(item => ({ value: item[0], label: item[1] })); /** * Represents options for {@link GraphColoring} selector. */ export const SelectorGraphColoring: { value: GraphColoring; label: string }[] = // [...mapLabelColoring.entries()].map(item => ({ value: item[0], label: item[1] })); -/** - * Represents options for {@link GraphSizing} selector. - */ -export const SelectorGraphSizing: { value: GraphSizing; label: string }[] = // - [...mapLabelSizing.entries()].map(item => ({ value: item[0], label: item[1] })); - /** * Represents options for {@link CstType} selector. */