diff --git a/package-lock.json b/package-lock.json index ab0e168..4179a91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,22 @@ { - "name": "orgexplorer", + "name": "OrgExplorer", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "orgexplorer", + "name": "OrgExplorer", "version": "0.0.0", "dependencies": { + "@tailwindcss/vite": "^4.2.2", + "axios": "^1.14.0", + "d3-force": "^3.0.0", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "react-force-graph-2d": "^1.29.1", + "react-icons": "^5.6.0", + "react-router-dom": "^7.13.2", + "recharts": "^3.8.1" }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -21,6 +28,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "tailwindcss": "^4.2.2", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", "vite": "npm:rolldown-vite@7.2.5" @@ -312,7 +320,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -324,7 +331,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -335,7 +341,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -555,7 +560,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -566,7 +570,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -577,7 +580,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -587,14 +589,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -605,7 +605,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -622,7 +621,6 @@ "version": "0.97.0", "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.97.0.tgz", "integrity": "sha512-yH0zw7z+jEws4dZ4IUKoix5Lh3yhqIJWF9Dc8PWvhpo7U7O+lJrv7ZZL4BeRO0la8LBQFwcCewtLBnVV7hPe/w==", - "dev": true, "license": "MIT", "engines": { "node": "^20.19.0 || >=22.12.0" @@ -632,12 +630,47 @@ "version": "0.97.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.97.0.tgz", "integrity": "sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-beta.50", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.50.tgz", @@ -645,7 +678,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -662,7 +694,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -679,7 +710,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -696,7 +726,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -713,7 +742,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -730,7 +758,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -747,7 +774,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -764,7 +790,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -781,7 +806,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -798,7 +822,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -815,7 +838,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -832,7 +854,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -849,7 +870,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -866,7 +886,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -883,214 +902,806 @@ "dev": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "license": "MIT" }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, - "node_modules/@types/node": { - "version": "24.10.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", - "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", - "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", - "dev": true, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", "license": "MIT", "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", - "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", - "dev": true, - "license": "MIT", + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "detect-libc": "^2.0.3" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.54.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", - "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "debug": "^4.4.3" + "url": "https://opencollective.com/parcel" }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://opencollective.com/parcel" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", - "debug": "^4.4.3" - }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "url": "https://opencollective.com/parcel" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" - }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/parcel" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "node_modules/@tailwindcss/node/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tweenjs/tween.js": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", + "integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", + "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", "dev": true, "license": "MIT", "engines": { @@ -1273,6 +1884,15 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/accessor-fn": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz", + "integrity": "sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1336,6 +1956,23 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", + "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1353,6 +1990,16 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bezier-js": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-6.1.4.tgz", + "integrity": "sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1398,6 +2045,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1429,6 +2089,18 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvas-color-tracker": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/canvas-color-tracker/-/canvas-color-tracker-1.3.2.tgz", + "integrity": "sha512-ryQkDX26yJ3CXzb3hxUVNlg1NKE4REc5crLBq661Nxzr8TNd236SaEf2ffYLXyI5tSABSeguHLqcVq4vf9L3Zg==", + "license": "MIT", + "dependencies": { + "tinycolor2": "^1.6.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1440,10 +2112,19 @@ "supports-color": "^7.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/color-convert": { @@ -1466,6 +2147,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1480,6 +2173,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1499,9 +2205,260 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, + "license": "MIT" + }, + "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", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force-3d": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.6.tgz", + "integrity": "sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==", + "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.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "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", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-octree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.1.0.tgz", + "integrity": "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==", "license": "MIT" }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "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-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "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", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1520,6 +2477,12 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1527,16 +2490,38 @@ "dev": true, "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.283", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz", @@ -1544,6 +2529,74 @@ "dev": true, "license": "ISC" }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-toolkit": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz", + "integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1751,6 +2804,12 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1776,7 +2835,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1841,11 +2899,86 @@ "dev": true, "license": "ISC" }, + "node_modules/float-tooltip": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/float-tooltip/-/float-tooltip-1.7.5.tgz", + "integrity": "sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg==", + "license": "MIT", + "dependencies": { + "d3-selection": "2 - 3", + "kapsule": "^1.16", + "preact": "10" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/force-graph": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.51.2.tgz", + "integrity": "sha512-zZNdMqx8qIQGurgnbgYIUsdXxSfvhfRSIdncsKGv/twUOZpwCsk9hPHmdjdcme1+epATgb41G0rkIGHJ0Wydng==", + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "18 - 25", + "accessor-fn": "1", + "bezier-js": "3 - 6", + "canvas-color-tracker": "^1.3", + "d3-array": "1 - 3", + "d3-drag": "2 - 3", + "d3-force-3d": "2 - 3", + "d3-scale": "1 - 4", + "d3-scale-chromatic": "1 - 3", + "d3-selection": "2 - 3", + "d3-zoom": "2 - 3", + "float-tooltip": "^1.7", + "index-array-by": "1", + "kapsule": "^1.16", + "lodash-es": "4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1856,6 +2989,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -1866,6 +3008,43 @@ "node": ">=6.9.0" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1892,6 +3071,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1902,6 +3099,45 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -1929,6 +3165,16 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -1956,6 +3202,24 @@ "node": ">=0.8.19" } }, + "node_modules/index-array-by": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/index-array-by/-/index-array-by-1.4.2.tgz", + "integrity": "sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "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/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1986,11 +3250,28 @@ "dev": true, "license": "ISC" }, + "node_modules/jerrypick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/jerrypick/-/jerrypick-1.1.2.tgz", + "integrity": "sha512-YKnxXEekXKzhpf7CLYA0A+oDP8V0OhICNCr5lv96FvSsDEmrb0GKM776JgQvHTMjr7DTTPEVv/1Ciaw0uEWzBA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -2053,6 +3334,18 @@ "node": ">=6" } }, + "node_modules/kapsule": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/kapsule/-/kapsule-1.16.3.tgz", + "integrity": "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg==", + "license": "MIT", + "dependencies": { + "lodash-es": "4" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2081,7 +3374,6 @@ "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", - "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -2114,7 +3406,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2135,7 +3426,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2156,7 +3446,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2177,7 +3466,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2198,7 +3486,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2219,7 +3506,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2240,7 +3526,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2261,7 +3546,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2282,7 +3566,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2303,7 +3586,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2324,7 +3606,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2354,6 +3635,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2361,6 +3648,18 @@ "dev": true, "license": "MIT" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2371,6 +3670,45 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2395,7 +3733,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -2424,6 +3761,15 @@ "dev": true, "license": "MIT" }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2511,14 +3857,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2531,7 +3875,6 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2556,6 +3899,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.29.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.0.tgz", + "integrity": "sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2566,6 +3919,32 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2597,6 +3976,77 @@ "react": "^19.2.4" } }, + "node_modules/react-force-graph-2d": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/react-force-graph-2d/-/react-force-graph-2d-1.29.1.tgz", + "integrity": "sha512-1Rl/1Z3xy2iTHKj6a0jRXGyiI86xUti81K+jBQZ+Oe46csaMikp47L5AjrzA9hY9fNGD63X8ffrqnvaORukCuQ==", + "license": "MIT", + "dependencies": { + "force-graph": "^1.51", + "prop-types": "15", + "react-kapsule": "^2.5" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-icons": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz", + "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "license": "MIT", + "peer": true + }, + "node_modules/react-kapsule": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/react-kapsule/-/react-kapsule-2.5.7.tgz", + "integrity": "sha512-kifAF4ZPD77qZKc4CKLmozq6GY1sBzPEJTIJb0wWFK6HsePJatK3jXplZn2eeAt3x67CDozgi7/rO8fNQ/AL7A==", + "license": "MIT", + "dependencies": { + "jerrypick": "^1.1.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", @@ -2607,6 +4057,95 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz", + "integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz", + "integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/recharts": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.1.tgz", + "integrity": "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2621,7 +4160,6 @@ "version": "1.0.0-beta.50", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.50.tgz", "integrity": "sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==", - "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.97.0", @@ -2654,7 +4192,6 @@ "version": "1.0.0-beta.50", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz", "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==", - "dev": true, "license": "MIT" }, "node_modules/scheduler": { @@ -2673,6 +4210,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2700,7 +4243,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -2732,11 +4274,41 @@ "node": ">=8" } }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2766,7 +4338,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true }, @@ -2825,7 +4396,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/update-browserslist-db": { @@ -2869,12 +4440,42 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/vite": { "name": "rolldown-vite", "version": "7.2.5", "resolved": "https://registry.npmjs.org/rolldown-vite/-/rolldown-vite-7.2.5.tgz", "integrity": "sha512-u09tdk/huMiN8xwoiBbig197jKdCamQTtOruSalOzbqGje3jdHiV0njQlAW0YvzoahkirFePNQ4RYlfnRQpXZA==", - "dev": true, "license": "MIT", "dependencies": { "@oxc-project/runtime": "0.97.0", diff --git a/package.json b/package.json index d75669c..7a383ea 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,15 @@ "preview": "vite preview" }, "dependencies": { + "@tailwindcss/vite": "^4.2.2", + "axios": "^1.14.0", + "d3-force": "^3.0.0", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "react-force-graph-2d": "^1.29.1", + "react-icons": "^5.6.0", + "react-router-dom": "^7.13.2", + "recharts": "^3.8.1" }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -23,6 +30,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "tailwindcss": "^4.2.2", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", "vite": "npm:rolldown-vite@7.2.5" diff --git a/src/App.css b/src/App.css index 027945e..3d4e1e9 100644 --- a/src/App.css +++ b/src/App.css @@ -1,6 +1,58 @@ +/* #root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} */ +/* @import "tailwindcss"; */ #root { max-width: 1280px; margin: 0 auto; padding: 2rem; text-align: center; +} + +body { + background: #0f172a; + color: #e2e8f0; + font-family: "Inter", sans-serif; +} + +.chart-container { + margin-top: 30px; + padding: 20px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.05); + backdrop-filter: blur(10px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +h1 { + font-size: 32px; + margin-bottom: 20px; +} + +h2 { + margin-bottom: 10px; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; +} + +th, td { + padding: 10px; + border-bottom: 1px solid #444; + text-align: left; +} + +th { + cursor: pointer; + color: #38bdf8; +} + +tr:hover { + background: rgba(255,255,255,0.05); } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 0a3deb1..470c721 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,37 @@ -import './App.css' +import { useState } from "react"; +import { Routes, Route } from "react-router-dom"; -function App() { +import DashboardLayout from "./layout/DashboardLayout"; +import Overview from "./pages/Overview"; +import Repositories from "./pages/Repositories"; +import GraphPage from "./pages/GraphPage"; +import ContributorDetail from "./pages/ContributorDetail"; +import RepoDetails from "./pages/RepoDetails"; + +export default function App() { + const [orgInput, setOrgInput] = useState(""); + const [orgLogo, setOrgLogo] = useState(""); return ( - <> -

Hello, OrgExplorer!

- - ) -} + + + + } + /> + + } /> + } /> + } /> + } /> -export default App + + + ); +} \ No newline at end of file diff --git a/src/components/Cards/statCard.tsx b/src/components/Cards/statCard.tsx new file mode 100644 index 0000000..2bca9ee --- /dev/null +++ b/src/components/Cards/statCard.tsx @@ -0,0 +1,13 @@ +interface Props { + title: string; + value: string | number; +} + +export default function StatCard({ title, value }: Props) { + return ( +
+

{title}

+

{value}

+
+ ); +} \ No newline at end of file diff --git a/src/components/Charts/ActivityChart.tsx b/src/components/Charts/ActivityChart.tsx new file mode 100644 index 0000000..7a7069d --- /dev/null +++ b/src/components/Charts/ActivityChart.tsx @@ -0,0 +1,313 @@ +import { useEffect, useState } from "react"; +import { + AreaChart, Area, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, + BarChart, Bar +} from "recharts"; +import { GoAlert, GoCheckbox } from "react-icons/go"; + + +const TOKEN = import.meta.env.VITE_GITHUB_TOKEN; + +export default function ActivityChart({ orgs }: { orgs: string[] }) { + + const [data, setData] = useState([]); + const [filter, setFilter] = useState("30"); + const [loading, setLoading] = useState(false); + + const getDays = () => (filter === "7" ? 7 : 30); + + useEffect(() => { + if (!orgs || orgs.length === 0) return; + + const timer = setTimeout(() => { + fetchData(); + }, 500); + + return () => clearTimeout(timer); + + }, [filter, orgs]); + + + const fetchAllPages = async (url: string) => { + let results: any[] = []; + let page = 1; + + while (page <= 3) { // limit pages (safe for rate limit) + const res = await fetch(`${url}&page=${page}`, { + headers: { Authorization: `token ${TOKEN}` } + }); + + const data = await res.json(); + + if (!data.items || data.items.length === 0) break; + + results.push(...data.items); + page++; + } + + return results; + }; + + const updateChart = (prs: any[], issues: any[]) => { + const days = getDays(); + const now = new Date(); + + setData(prev => { + const map: any = {}; + + prev.forEach(d => { + map[d.date] = { ...d }; + }); + + prs.forEach((pr: any) => { + const date = new Date(pr.created_at); + const diff = (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24); + + if (diff <= days) { + const key = date.toISOString().split("T")[0]; + if (!map[key]) return; + + map[key].prCreated += 1; + if (pr.state === "closed") map[key].prMerged += 1; + } + }); + + issues.forEach((issue: any) => { + const date = new Date(issue.created_at); + const diff = (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24); + + if (diff <= days) { + const key = date.toISOString().split("T")[0]; + if (!map[key]) return; + + map[key].issuesCreated += 1; + if (issue.state === "closed") map[key].issuesClosed += 1; + } + }); + + return Object.values(map); + }); + }; + + const fetchData = async () => { + try { + setLoading(true); + setData( + Array.from({ length: getDays() + 1 }, (_, i) => { + const d = new Date(); + d.setDate(d.getDate() - (getDays() - i)); + const key = d.toISOString().split("T")[0]; + + return { + date: key, + prCreated: 0, + prMerged: 0, + issuesCreated: 0, + issuesClosed: 0 + }; + }) + ); + + const days = getDays(); + const now = new Date(); + + let allPRs: any[] = []; + let allIssues: any[] = []; + + // MULTI ORG + PAGINATION + const orgPromises = orgs.map(async (org) => { + + const prs = await fetchAllPages( + `https://api.github.com/search/issues?q=org:${org}+type:pr&per_page=100` + ); + + const issues = await fetchAllPages( + `https://api.github.com/search/issues?q=org:${org}+type:issue&per_page=100` + ); + + return { prs, issues }; + }); + + // const results = await Promise.all(orgPromises); + + orgs.forEach(async (org) => { + try { + const prs = await fetchAllPages( + `https://api.github.com/search/issues?q=org:${org}+type:pr&per_page=100` + ); + + const issues = await fetchAllPages( + `https://api.github.com/search/issues?q=org:${org}+type:issue&per_page=100` + ); + + // update chart immediately for THIS org + updateChart(prs, issues); + + } catch (err) { + console.error(err); + } + }); + + // results.forEach(r => { + // allPRs.push(...r.prs); + // allIssues.push(...r.issues); + // }); + + const chartMap: any = {}; + + // PR DATA + allPRs.forEach((pr: any) => { + const date = new Date(pr.created_at); + const diff = (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24); + + if (diff <= days) { + const key = date.toISOString().split("T")[0]; + + if (!chartMap[key]) { + chartMap[key] = { + date: key, + prCreated: 0, + prMerged: 0, + issuesCreated: 0, + issuesClosed: 0 + }; + } + + chartMap[key].prCreated++; + if (pr.state === "closed") chartMap[key].prMerged++; + } + }); + + // ISSUE DATA + allIssues.forEach((issue: any) => { + const date = new Date(issue.created_at); + const diff = (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24); + + if (diff <= days) { + const key = date.toISOString().split("T")[0]; + + if (!chartMap[key]) { + chartMap[key] = { + date: key, + prCreated: 0, + prMerged: 0, + issuesCreated: 0, + issuesClosed: 0 + }; + } + + chartMap[key].issuesCreated++; + if (issue.state === "closed") chartMap[key].issuesClosed++; + } + }); + + // FILL DAYS (NO BREAK LINES) + const result: any[] = []; + + for (let i = days; i >= 0; i--) { + const d = new Date(); + d.setDate(d.getDate() - i); + + const key = d.toISOString().split("T")[0]; + + result.push({ + date: key, + prCreated: chartMap[key]?.prCreated || 0, + prMerged: chartMap[key]?.prMerged || 0, + issuesCreated: chartMap[key]?.issuesCreated || 0, + issuesClosed: chartMap[key]?.issuesClosed || 0 + }); + } + + console.log("FINAL COMBINED:", result); + + setData(result); + + } catch (err) { + console.error(err); + } finally { + setLoading(false); + } + }; + const formatDate = (date: string) => { + const d = new Date(date); + return d.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + }; + + // INSIGHT + const totalPR = data.reduce((a, b) => a + b.prCreated, 0); + const mergedPR = data.reduce((a, b) => a + b.prMerged, 0); + const mergeRate = totalPR ? Math.round((mergedPR / totalPR) * 100) : 0; + + return ( +
+ + {/* TITLE */} +

+ {orgs.length > 1 + ? `Combined Analytics (${orgs.length} Organizations)` + : ` Activity (${orgs[0]})`} +

+ + {/* FILTER */} +
+ + +
+ + {/* LOADING */} + {loading && ( +

Loading data...

+ )} + + {/* INSIGHT */} +

+ {mergeRate > 70 + ? ( PR merge rate is strong) + : ( PR merge rate dropped recently)} +

+ + {/* CHARTS */} +
+ + {/* AREA CHART */} +
+

PR Trend

+ + + + + + + + + + + + +
+ + {/* BAR CHART */} +
+

Issues Activity

+ + + + + + + + + + + + +
+ +
+
+ ); +} \ No newline at end of file diff --git a/src/components/Graph/NetworkGraph.tsx b/src/components/Graph/NetworkGraph.tsx new file mode 100644 index 0000000..68fce2f --- /dev/null +++ b/src/components/Graph/NetworkGraph.tsx @@ -0,0 +1,549 @@ +import ForceGraph2D from "react-force-graph-2d"; +import { useEffect, useRef, useState } from "react"; +import { fetchRepoContributors } from "../../services/githubService"; +import { exportCSV } from "../../utils/exportCSV"; +import { GoPeople, GoRepo, GoGitBranch, GoLink } from "react-icons/go"; +import { FaLink } from "react-icons/fa"; +import { IoPeople } from "react-icons/io5"; + + +type NodeType = { + id: string; + type: "repo" | "user"; + label: string; + img?: string; + + stars?: number; + forks?: number; + issues?: number; + + contributions?: number; + activity?: number; + + size?: number; + org?: string; +}; + +type LinkType = { + source: string; + target: string; + weight: number; +}; + +export default function NetworkGraph({ repos }: any) { + const fgRef = useRef(null); + + const [graphData, setGraphData] = useState<{ + nodes: NodeType[]; + links: LinkType[]; + }>({ nodes: [], links: [] }); + + const [selectedNode, setSelectedNode] = useState(null); + const [stats, setStats] = useState({ + users: 0, + repos: 0, + edges: 0, + }); + const [hoverNode, setHoverNode] = useState(null); + const [focusNode, setFocusNode] = useState(null); + + /* ───────── BUILD GRAPH (MULTI ORG FIX) ───────── */ + useEffect(() => { + if (!repos?.length) return; + + const build = async () => { + const nodes: NodeType[] = []; + const links: LinkType[] = []; + const userMap = new Map(); + + // GROUP BY ORG + const orgGrouped: Record = {}; + + repos.forEach((repo: any) => { + const org = repo.full_name.split("/")[0]; + if (!orgGrouped[org]) orgGrouped[org] = []; + orgGrouped[org].push(repo); + }); + + // TAKE 4 REPOS PER ORG + const selectedRepos: any[] = []; + Object.values(orgGrouped).forEach((list: any[]) => { + selectedRepos.push(...list.slice(0, 4)); + }); + + for (const repo of selectedRepos) { + const org = repo.full_name.split("/")[0]; + + // REPO NODE + nodes.push({ + id: repo.full_name, + type: "repo", + label: repo.name, + org, + stars: repo.stargazers_count, + forks: repo.forks_count, + issues: repo.open_issues_count, + activity: new Date(repo.updated_at).getTime(), + size: + Math.log((repo.stargazers_count || 1) + 1) * 4 + + Math.log((repo.forks_count || 1) + 1) * 2 + + 8, + }); + + try { + const contributors = await fetchRepoContributors( + `${repo.contributors_url}?per_page=30` + ); + + contributors.slice(0, 15).forEach((c: any) => { + if (!c?.login) return; + + if (!userMap.has(c.login)) { + userMap.set(c.login, true); + + nodes.push({ + id: c.login, + type: "user", + label: c.login, + img: c.avatar_url, + contributions: c.contributions, + activity: c.contributions, + size: Math.log((c.contributions || 1) + 1) * 3 + 6, + }); + } + + links.push({ + source: c.login, + target: repo.full_name, + weight: c.contributions || 1, + }); + }); + } catch (err) { + console.log(err); + } + } + setStats({ + users: nodes.filter(n => n.type === "user").length, + repos: nodes.filter(n => n.type === "repo").length, + edges: links.length, + }); + setGraphData({ nodes, links }); + }; + + + build(); + }, [repos]); + + + /* ───────── FORCE LAYOUT (MULTI ORG FIXED) ───────── */ + useEffect(() => { + if (!fgRef.current) return; + + const fg = fgRef.current; + + fg.d3Force("charge").strength(-320); + + fg.d3Force("link").distance((l: any) => + Math.max(80, 200 - Math.log2(l.weight + 1) * 25) + ); + + // ORGS + const orgs: string[] = Array.from( + new Set( + graphData.nodes + .filter((n) => n.type === "repo" && n.org) + .map((n) => n.org as string) + ) + ); + + const orgMap: Record = {}; + const gap = 400; + + orgs.forEach((org, i) => { + orgMap[org] = (i - (orgs.length - 1) / 2) * gap; + }); + + // X FORCE + fg.d3Force("x", (node: any) => { + if (node.type === "repo") { + return node.org ? orgMap[node.org] ?? 0 : 0; + } + + const linked = graphData.links.find((l) => l.source === node.id); + + if (!linked) return 0; + + const repoNode = graphData.nodes.find( + (n) => n.id === linked.target + ) as NodeType | undefined; + + if (!repoNode || !repoNode.org) return 0; + + return orgMap[repoNode.org] ?? 0; + }); + + // Y FORCE (activity) + fg.d3Force("y", (node: any) => { + const act = node.activity || 1; + const norm = Math.min(1, Math.log(act + 1) / 10); + + return node.type === "repo" + ? -300 * norm + : 300 * (1 - norm); + }); + }, [graphData]); + + // ======================================================================================== + const isConnectedToFocus = (nodeId: string) => { + if (!focusNode) return true; + + const focusId = focusNode.id; + + return graphData.links.some((l: any) => { + const s = typeof l.source === "object" ? l.source.id : l.source; + const t = typeof l.target === "object" ? l.target.id : l.target; + + return ( + (s === nodeId && t === focusId) || + (t === nodeId && s === focusId) + ); + }); + }; + // ======================================================= + /* ───────── NODE DRAW ───────── */ + + const drawNode = (node: any, ctx: CanvasRenderingContext2D, scale: number) => { + const padding = 6; + + // dynamic width based on text + const label = node.label || ""; + ctx.font = `${9 / scale}px Arial`; + const textWidth = ctx.measureText(label).width; + + const width = Math.max(80, textWidth + 40); // auto width + const height = 52; + + const x = node.x - width / 2; + const y = node.y - height / 2; + + // helper for id (IMPORTANT FIX) + const getId = (val: any) => + typeof val === "object" ? val.id : val; + + let opacity = 1; + + // PRIORITY: FOCUS MODE + if (focusNode) { + const isConnected = isConnectedToFocus(node.id); + + if (node.id !== focusNode.id && !isConnected) { + opacity = 0.1; + } + } + + // SECOND: HOVER MODE + else if (hoverNode) { + const getId = (val: any) => + typeof val === "object" ? val.id : val; + + const isConnected = graphData.links.some((l: any) => { + const s = getId(l.source); + const t = getId(l.target); + + return ( + (s === node.id && t === hoverNode.id) || + (t === node.id && s === hoverNode.id) + ); + }); + + if (node.id !== hoverNode.id && !isConnected) { + opacity = 0.1; + } + } + + ctx.globalAlpha = opacity; + + // CARD BACKGROUND (glass style) + ctx.fillStyle = "rgba(15, 23, 42, 0.9)"; + ctx.strokeStyle = node.type === "repo" ? "#facc15" : "#22c55e"; + ctx.lineWidth = 1.5; + + ctx.beginPath(); + ctx.roundRect(x, y, width, height, 10); + ctx.fill(); + ctx.stroke(); + + // avatar + if (node.img) { + const img = new Image(); + img.src = node.img; + + ctx.save(); + ctx.beginPath(); + ctx.roundRect(x + padding, y + padding, 22, 22, 4); + ctx.clip(); + ctx.drawImage(img, x + padding, y + padding, 22, 22); + ctx.restore(); + } + + // TEXT CLIP (IMPORTANT — no overflow) + ctx.save(); + ctx.beginPath(); + ctx.rect(x + 30, y + 5, width - 35, 20); + ctx.clip(); + + ctx.fillStyle = "#e5e7eb"; + ctx.fillText(label, x + 30, y + 18); + ctx.restore(); + + // 📊 contributions / stats + if (node.contributions) { + ctx.fillStyle = "#22c55e"; + ctx.font = `${8 / scale}px Arial`; + ctx.fillText(`${node.contributions} commits`, x + 30, y + 35); + } + + if (node.type === "repo") { + ctx.fillStyle = "#facc15"; + ctx.font = `${8 / scale}px Arial`; + ctx.fillText("repo", x + 30, y + 35); + } + + ctx.globalAlpha = 1; + + if (focusNode) { + const isConnected = isConnectedToFocus(node.id); + + if (node.id !== focusNode.id && !isConnected) { + opacity = 0.1; // dim others + } + } + }; + /* ───────── LINK DRAW ───────── */ + + const topContributor = graphData.nodes + .filter(n => n.type === "user") + .sort((a, b) => (b.contributions || 0) - (a.contributions || 0))[0]; + + const mostActiveRepo = graphData.nodes + .filter(n => n.type === "repo") + .sort((a, b) => (b.activity || 0) - (a.activity || 0))[0]; + + const strongestLink = graphData.links + .sort((a, b) => b.weight - a.weight)[0]; + + const getId = (val: any) => + typeof val === "object" ? val.id : val; + + // TOTAL UNIQUE CONTRIBUTORS + const totalUniqueContributors = new Set( + graphData.nodes + .filter(n => n.type === "user") + .map(n => n.id) + ).size; + + + // SHARED CONTRIBUTORS + const contributorOrgs: Record> = {}; + + graphData.links.forEach((l: any) => { + const user = typeof l.source === "object" ? l.source.id : l.source; + const repo = graphData.nodes.find(n => n.id === l.target); + + if (!repo?.org) return; + + if (!contributorOrgs[user]) { + contributorOrgs[user] = new Set(); + } + + contributorOrgs[user].add(repo.org); + }); + + const sharedContributors = Object.values(contributorOrgs).filter( + (orgSet) => orgSet.size > 1 + ).length; + + const drawLink = (link: any, ctx: CanvasRenderingContext2D) => { + const getId = (val: any) => + typeof val === "object" ? val.id : val; + + const s = link.source; + const t = link.target; + + if (!s?.x || !t?.x) return; + + let opacity = 0.3; + + if (focusNode) { + const sourceId = getId(link.source); + const targetId = getId(link.target); + + if (sourceId === focusNode.id || targetId === focusNode.id) { + opacity = 1; + } else { + opacity = 0.05; + } + } else if (hoverNode) { + const sourceId = getId(link.source); + const targetId = getId(link.target); + + if (sourceId === hoverNode.id || targetId === hoverNode.id) { + opacity = 1; + } else { + opacity = 0.05; + } + } + + ctx.beginPath(); + ctx.moveTo(s.x, s.y); + ctx.lineTo(t.x, t.y); + + ctx.strokeStyle = `rgba(34,197,94,${opacity})`; + ctx.lineWidth = Math.max(1, Math.log2(link.weight + 1)); + + ctx.stroke(); + }; + /* ───────── UI ───────── */ + + return ( +
+ +
+ + {/* LEFT SIDE (FULL WIDTH STATS) */} +
+ +
+ {stats.users} Contributors +
+ +
+ {stats.repos} Repos +
+ +
+ {stats.edges} Links +
+ +
+ {totalUniqueContributors} Total +
+ +
+ {sharedContributors} Shared +
+ +
+ Top Contributor : {topContributor?.label} +
+ +
+ Most Active Repo : {mostActiveRepo?.label} +
+ +
+ {getId(strongestLink?.source)} → {getId(strongestLink?.target)} +
+ +
+ + {/* RIGHT SIDE BUTTON */} + + +
+ + {selectedNode && ( +
+ + + +
+ {selectedNode.img && ( + + )} +
+

{selectedNode.label}

+

+ {selectedNode.type === "user" ? "Contributor" : "Repository"} +

+
+
+ + {selectedNode.type === "user" ? ( + <> +

Commits: {selectedNode.contributions}

+ + ) : ( + <> +

⭐ Stars: {selectedNode.stars}

+

🍴 Forks: {selectedNode.forks}

+ + )} + + + Open GitHub → + +
+ + + )} + + { + const width = 100; + const height = 60; + + ctx.fillStyle = color; + ctx.fillRect( + node.x - width / 2, + node.y - height / 2, + width, + height + ); + }} + + onNodeHover={(node: any) => { + setHoverNode(node); + document.body.style.cursor = node ? "pointer" : "default"; + }} + + onNodeClick={(node: any) => { + setSelectedNode(node); + setFocusNode(node); + }} + + linkDirectionalParticles={2} + linkDirectionalParticleSpeed={0.004} + + onEngineStop={() => fgRef.current?.zoomToFit(400)} + /> +
+ ); +} diff --git a/src/components/HealthScore.tsx b/src/components/HealthScore.tsx new file mode 100644 index 0000000..dae57f4 --- /dev/null +++ b/src/components/HealthScore.tsx @@ -0,0 +1,28 @@ +export default function HealthScore({ score, label }: any) { + return ( +
+ +

+ Organization Health Score +

+ +
+ + {score}/100 + + + {label} + +
+ + {/* Progress bar */} +
+
+
+ +
+ ); +} \ No newline at end of file diff --git a/src/components/Input/OrgInput.tsx b/src/components/Input/OrgInput.tsx new file mode 100644 index 0000000..8445dd6 --- /dev/null +++ b/src/components/Input/OrgInput.tsx @@ -0,0 +1,19 @@ +export default function OrgInput({ onSubmit, value, setValue }: any) { + return ( +
+ setValue(e.target.value)} + /> + + +
+ ); +} \ No newline at end of file diff --git a/src/components/Insights/Insightpanel.tsx b/src/components/Insights/Insightpanel.tsx new file mode 100644 index 0000000..bb3a0ad --- /dev/null +++ b/src/components/Insights/Insightpanel.tsx @@ -0,0 +1,59 @@ + +import StatCard from "../Cards/statCard"; +import { IoIosStarOutline } from "react-icons/io" +import { GoRepo } from "react-icons/go"; +import { useNavigate } from "react-router-dom"; + +export default function InsightPanel({ data }: { data: any }) { + const navigate = useNavigate(); + return ( + <> +
+ + + + +
+
+

High Activity Repositories

+ +
+ + + {data.topRepos.map((repo: any, i: number) => ( +
navigate(`/repo/${repo.name}`, { state: repo })} + className="flex justify-between items-center p-2 rounded hover:bg-gray-800 transition cursor-pointer" + > + + {repo.name} + + + + {repo.stargazers_count} + +
+ ))} +
+
+ +
+

+ Key Insights +

+ +
+ {data.insights.map((insight: string, i: number) => ( +
+ {insight} +
+ ))} +
+
+ + ); +} diff --git a/src/components/Tables/RepoTable.tsx b/src/components/Tables/RepoTable.tsx new file mode 100644 index 0000000..4ad9287 --- /dev/null +++ b/src/components/Tables/RepoTable.tsx @@ -0,0 +1,191 @@ +import { useState } from "react"; +import { FaCodeBranch } from "react-icons/fa"; +import { MdOutlineReportGmailerrorred } from "react-icons/md"; +import { useNavigate } from "react-router-dom"; +import { IoIosStarOutline } from "react-icons/io"; +import { GoXCircle, GoCheckCircleFill } from "react-icons/go"; + +console.log("Rendering RepoTable component..."); +export default function RepoTable({ repos }: any) { + const [sortKey, setSortKey] = useState("stars"); + const [search, setSearch] = useState(""); + const [language, setLanguage] = useState("all"); + + const navigate = useNavigate(); + + // UNIQUE LANGUAGES + const languages = [ + "all", + ...new Set(repos.map((r: any) => r.language).filter(Boolean)), + ]; + + // FILTER + SEARCH + const filtered = repos.filter((r: any) => { + const matchSearch = + r.name.toLowerCase().includes(search.toLowerCase()) || + (r.language || "").toLowerCase().includes(search.toLowerCase()); + + const matchLang = + language === "all" || r.language === language; + + return matchSearch && matchLang; + }); + + // SORT + const sorted = [...filtered].sort((a, b) => { + if (sortKey === "stars") return b.stargazers_count - a.stargazers_count; + if (sortKey === "forks") return b.forks_count - a.forks_count; + if (sortKey === "issues") return b.open_issues_count - a.open_issues_count; + return 0; + }); + + // STATUS + const getStatus = (updated_at: string) => { + const days = + (Date.now() - new Date(updated_at).getTime()) / + (1000 * 60 * 60 * 24); + + return days < 30 ? "Active" : "Inactive"; + }; + + return ( +
+ + {/* TOP BAR */} +
+ + {/* TITLE */} +

+ Repositories ({sorted.length}) +

+ + {/* SEARCH */} + setSearch(e.target.value)} + className="px-3 py-2 rounded bg-gray-800 text-white border border-gray-600" + /> + + {/* LANGUAGE FILTER */} + +
+ + {/* SORT BUTTONS */} +
+ + + + + +
+ + {/* TABLE */} +
+ + + + + + + + + + + + + + + + {sorted.map((r: any) => ( + navigate(`/repo/${r.name}`, { state: r })} + className="border-t border-gray-700 hover:bg-gray-800 transition cursor-pointer" + > + {/* 1. REPO */} + + + {/* 2. STARS */} + + + {/* 3. FORKS */} + + + {/* 4. ISSUES */} + + + {/* 5. LANGUAGE */} + + + {/* 6. LAST UPDATED */} + + + {/* 7. STATUS */} + + + ))} + +
Repository Stars Forks IssuesLanguageLast UpdatedStatus
+ {r.name} +
+ {r.description || "No description"} +
+
+
+ {r.stargazers_count} +
+
+
+ {r.forks_count} +
+
+
+ {r.open_issues_count || 0} +
+
+ + {r.language || "N/A"} + + + {new Date(r.updated_at).toLocaleDateString()} + + {getStatus(r.updated_at).includes("Active") ? ( + Active + ) : ( + Inactive + )} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/TopContributors.tsx b/src/components/TopContributors.tsx new file mode 100644 index 0000000..37c3a8d --- /dev/null +++ b/src/components/TopContributors.tsx @@ -0,0 +1,122 @@ +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { FaTrophy } from "react-icons/fa"; + +const TOKEN = import.meta.env.VITE_GITHUB_TOKEN; + +export default function TopContributors({ repos }: any) { + const [contributors, setContributors] = useState([]); + const navigate = useNavigate(); + + useEffect(() => { + console.log("Fetching data for:", repos); + + const fetchData = async () => { + const map: any = {}; + + // GROUPING START + const groupedRepos: any = {}; + + repos.forEach((repo: any) => { + const org = repo.full_name.split("/")[0]; + + if (!groupedRepos[org]) { + groupedRepos[org] = []; + } + + groupedRepos[org].push(repo); + }); + + // EACH ORG SE LIMIT + let finalRepos: any[] = []; + + Object.values(groupedRepos).forEach((orgRepos: any) => { + finalRepos.push(...orgRepos.slice(0, 10)); + }); + // DEBUG + console.log( + "Repos being used:", + finalRepos.map((r: any) => r.full_name) + ); + + // MAIN LOOP (IMPORTANT) + for (let repo of finalRepos) { + try { + const res = await fetch(repo.contributors_url, { + headers: { + Authorization: `token ${TOKEN}` + } + }); + + const data = await res.json(); + + if (!Array.isArray(data)) { + console.error("GitHub API Error:", data); + continue; + } + + data.forEach((c: any) => { + if (!map[c.login]) { + map[c.login] = { + login: c.login, + avatar: c.avatar_url, + contributions: 0, + url: c.html_url + }; + } + + map[c.login].contributions += c.contributions; + }); + + } catch (e) { + console.error("Fetch error:", e); + } + } + + const sorted = Object.values(map) + .sort((a: any, b: any) => b.contributions - a.contributions) + .slice(0, 5); + + setContributors(sorted); + }; + + if (repos.length) fetchData(); + }, [repos]); + + return ( +
+

+ + Top Contributors +

+ + {/* Horizontal Scroll */} +
+ + {contributors.map((c, i) => ( +
+ navigate(`/contributor/${c.login}`, { state: c }) + } + className="min-w-[220px] bg-[#111827] p-4 rounded-lg cursor-pointer hover:bg-gray-800 transition border border-gray-700" + > + + +

+ #{i + 1} {c.login} +

+ +

+ {c.contributions} contributions +

+
+ ))} + +
+
+ ); +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index e0dbee4..20f3253 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,13 @@ +@import "tailwindcss"; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + @apply bg-slate-900 text-white; +} + :root { font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; diff --git a/src/layout/DashboardLayout.tsx b/src/layout/DashboardLayout.tsx new file mode 100644 index 0000000..e642122 --- /dev/null +++ b/src/layout/DashboardLayout.tsx @@ -0,0 +1,23 @@ +import Sidebar from "./Sidebar"; +import Topbar from "./Topbar"; + +export default function DashboardLayout({ children, orgInput, orgLogo }: any) { + return ( +
+ + {/* Sidebar */} + + + {/* Main */} +
+ + + +
+ {children} +
+ +
+
+ ); +} \ No newline at end of file diff --git a/src/layout/Sidebar.tsx b/src/layout/Sidebar.tsx new file mode 100644 index 0000000..acb1063 --- /dev/null +++ b/src/layout/Sidebar.tsx @@ -0,0 +1,55 @@ + +import { Link, useLocation } from "react-router-dom"; + + +export default function Sidebar() { + + const location = useLocation(); + + return ( +
+ +
+
+ Logo +
+ + +
+ + +
+ ); +} \ No newline at end of file diff --git a/src/layout/Topbar.tsx b/src/layout/Topbar.tsx new file mode 100644 index 0000000..3ec5e2f --- /dev/null +++ b/src/layout/Topbar.tsx @@ -0,0 +1,33 @@ +import { useLocation } from "react-router-dom"; + +export default function Topbar({ orgInput, logo }: any) { + const location = useLocation(); + + // Only dashboard + if (location.pathname !== "/") return null; + + const orgs = orgInput + ? orgInput.split(",").map((o: string) => o.trim()) + : []; + + return ( +
+ +
+ {logo ? ( + + ) : ( +
+ )} + +

+ {orgs.join(" + ") || "OrgExplorer"} +

+
+ +
+ ); +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index bef5202..f40b154 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,9 +2,13 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' import App from './App.tsx' +import { BrowserRouter } from "react-router-dom"; createRoot(document.getElementById('root')!).render( - + + + + , -) +) \ No newline at end of file diff --git a/src/pages/ContributorDetail.tsx b/src/pages/ContributorDetail.tsx new file mode 100644 index 0000000..109cdd4 --- /dev/null +++ b/src/pages/ContributorDetail.tsx @@ -0,0 +1,508 @@ +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { + AreaChart, Area, XAxis, YAxis, Tooltip, CartesianGrid, ResponsiveContainer, Legend +} from "recharts"; +import { FaCodeBranch, FaGithub } from "react-icons/fa"; +import { FaCodeMerge } from "react-icons/fa6"; +import { GoIssueOpened, GoIssueClosed,GoCheckCircleFill, GoXCircle, GoAlert, GoStar, GoGitMerge, GoTrophy, GoPeople } from "react-icons/go"; +// import { MdOutlineReportGmailerrorred } from "react-icons/md"; + + +const TOKEN = import.meta.env.VITE_GITHUB_TOKEN; + +export default function ContributorDetail() { + const { username } = useParams(); + + const [user, setUser] = useState(null); + const [prs, setPrs] = useState([]); + const [issues, setIssues] = useState([]); + const [sortKey, setSortKey] = useState("prCreated"); + const [sortOrder, setSortOrder] = useState("desc"); + const [events, setEvents] = useState([]); + const [filter, setFilter] = useState("7"); + + useEffect(() => { + + if (!username) return; + + const fetchData = async () => { + // console.log("Repos coming:", repos.map(r => r.full_name)); + try { + // USER + const userRes = await fetch(`https://api.github.com/users/${username}`, { + headers: { Authorization: `token ${TOKEN}` } + }); + const userData = await userRes.json(); + setUser(userData); + + // PRs + const prRes = await fetch( + `https://api.github.com/search/issues?q=author:${username}+type:pr`, + { headers: { Authorization: `token ${TOKEN}` } } + ); + const prData = await prRes.json(); + setPrs(prData.items || []); + + // Issues + const issueRes = await fetch( + `https://api.github.com/search/issues?q=author:${username}+type:issue`, + { headers: { Authorization: `token ${TOKEN}` } } + ); + const issueData = await issueRes.json(); + setIssues(issueData.items || []); + + // EVENTS (Recent Activity) + const eventRes = await fetch( + `https://api.github.com/users/${username}/events`, + { headers: { Authorization: `token ${TOKEN}` } } + ); + const eventData = await eventRes.json(); + setEvents(eventData || []); + console.log("events", eventData); + + } catch (err) { + console.error(err); + } + }; + + fetchData(); + + + }, [username]); + + function timeAgo(date: string) { + const seconds = Math.floor((new Date().getTime() - new Date(date).getTime()) / 1000); + + const intervals: any = { + year: 31536000, + month: 2592000, + week: 604800, + day: 86400, + hour: 3600, + minute: 60, + }; + + for (let key in intervals) { + const interval = Math.floor(seconds / intervals[key]); + if (interval > 1) return `${interval} ${key}s ago`; + if (interval === 1) return `1 ${key} ago`; + } + + return "just now"; + } + + const filteredEvents = events.filter((e: any) => { + const days = filter === "7" ? 7 : 30; + const eventDate = new Date(e.created_at); + const now = new Date(); + + return (now.getTime() - eventDate.getTime()) / (1000 * 60 * 60 * 24) <= days; + }); + + if (!user) return

Loading...

; + + // CALCULATIONS + const totalPR = prs.length; + const mergedPR = prs.filter(p => p.pull_request?.merged_at).length; + const mergeRate = totalPR ? Math.round((mergedPR / totalPR) * 100) : 0; + const totalIssues = issues.length; + + const score = Math.round( + 0.5 * mergeRate + + 0.3 * Math.min(totalIssues * 5, 100) + + 0.2 * 70 + ); + + // REPO-WISE STATS + const repoStats: any = {}; + + // PR data + prs.forEach((pr: any) => { + // const repo = pr.repository_url.split("/").pop(); + const parts = pr.repository_url.split("/"); + const repo = parts[parts.length - 2] + "/" + parts[parts.length - 1]; + + if (!repoStats[repo]) { + repoStats[repo] = { + repo, + prCreated: 0, + prMerged: 0, + issuesSolved: 0, + }; + } + + repoStats[repo].prCreated++; + + if (pr.pull_request?.merged_at) { + repoStats[repo].prMerged++; + } + }); + + // Issue data + issues.forEach((issue: any) => { + // const repo = issue.repository_url.split("/").pop(); + const parts = issue.repository_url.split("/"); + const repo = parts[parts.length - 2] + "/" + parts[parts.length - 1]; + + if (!repoStats[repo]) { + repoStats[repo] = { + repo, + prCreated: 0, + prMerged: 0, + issuesSolved: 0, + }; + } + + repoStats[repo].issuesSolved++; + }); + + const repoList = Object.values(repoStats); + const sortedRepos = [...repoList].sort((a: any, b: any) => { + const valA = a[sortKey]; + const valB = b[sortKey]; + + if (sortOrder === "asc") return valA - valB; + return valB - valA; + }); + + const chartData = repoList.map((r: any) => ({ + name: r.repo.split("/")[1], // sirf repo name + created: r.prCreated, + merged: r.prMerged + })); + + return ( +
+ + {/* TOP SECTION */} +
+ + + +
+

{user.login}

+ +

+ Quality Score: {score}/100 +

+ +

+ Merge Rate: {mergeRate}% +

+ +

80 ? "text-green-400" : + score > 50 ? "text-yellow-400" : + "text-red-400" + }`}> + {score > 80 ? ( High Quality Contributor) : + score > 50 ? ( Medium Quality Contributor) : + (Low Quality Contributor)} +

+
+
+ + {/* STATS CARDS */} +
+
+
+ + PR +
+

{totalPR}

+
+ +
+
+ + PR Merged +
+ +

{mergedPR}

+
+ +
+
+ +

Issues Solved

+
+

{totalIssues}

+
+ +
+
+ +

Issues Created

+
+

{totalIssues}

+
+
+ +
+ +

+ PR Activity (Created vs Merged) +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + {/* REPOSITORIES TABLE */} +
+ +

+ Repository Contributions +

+ + {repoList.length === 0 ? ( +

No repository data found

+ ) : ( +
+ + + + + + + + + + + + + + + + + + {/* BODY */} + + {sortedRepos.map((r: any) => { + const mergeRate = + r.prCreated > 0 + ? Math.round((r.prMerged / r.prCreated) * 100) + : 0; + + return ( + + + + + + + + + + + + ); + })} + + +
Repo { + setSortKey("prCreated"); + setSortOrder(sortOrder === "asc" ? "desc" : "asc"); + }} + > + PR {sortKey === "prCreated" ? (sortOrder === "asc" ? "↑" : "↓") : ""} + { + setSortKey("prMerged"); + setSortOrder(sortOrder === "asc" ? "desc" : "asc"); + }} + > + PR Merged {sortKey === "prMerged" ? (sortOrder === "asc" ? "↑" : "↓") : ""} + Merge % { + setSortKey("issuesSolved"); + setSortOrder(sortOrder === "asc" ? "desc" : "asc"); + }} + > + Issues Solved {sortKey === "issuesSolved" ? (sortOrder === "asc" ? "↑" : "↓") : ""} +
+ + {r.repo} + + {r.prCreated} + {r.prMerged} + + {mergeRate}% + + {r.issuesSolved} +
+
+ )} +
+ + {/* INSIGHTS */} +
+

Insights

+ +
    + {mergeRate > 70 && ( +
  • High merge rate indicates quality work
  • + )} + {mergeRate < 40 && ( +
  • Low merge rate suggests PR issues
  • + )} + {totalPR < 5 && ( +
  • Limited contributions
  • + )} +
+
+ + + {/* Recent Activity */} +
+
+

Recent Activity

+ + {/* Filter */} + +
+ + {/* Events */} + {filteredEvents.length === 0 ? ( +

No recent activity

+ ) : ( +
+ {filteredEvents.slice(0, 10).map((e: any, index: number) => { + const isLatest = index === 0; + + let text = ""; + let link = "#"; + + // PR Created + if (e.type === "PullRequestEvent" && e.payload.action === "opened") { + const prNumber = e.payload?.pull_request?.number || ""; + text = ` Created PR #${prNumber} in ${e.repo.name}`; + link = e.payload?.pull_request?.html_url; + } + + // PR Merged + else if (e.type === "PullRequestEvent" && e.payload?.pull_request?.merged) { + const prNumber = e.payload?.pull_request?.number || ""; + text = ` PR #${prNumber} merged in ${e.repo.name}`; + link = e.payload?.pull_request?.html_url; + } + + // Issue Opened + else if (e.type === "IssuesEvent" && e.payload.action === "opened") { + const issueNumber = e.payload?.issue?.number || ""; + text = ` Opened issue #${issueNumber} in ${e.repo.name}`; + link = e.payload?.issue?.html_url; + } + + // Issue Closed + else if (e.type === "IssuesEvent" && e.payload.action === "closed") { + const issueNumber = e.payload?.issue?.number || ""; + text = ` Closed issue #${issueNumber} in ${e.repo.name}`; + link = e.payload?.issue?.html_url; + } + // Push + else if (e.type === "PushEvent") { + text = ` Pushed code to ${e.repo.name}`; + link = `https://github.com/${e.repo.name}`; + } + + else return null; + + return ( + + {text} + + ({timeAgo(e.created_at)}) + + + ); + })} +
+ )} +
+ + + + {/* GITHUB */} + + Open On Github + + +
+ ); +} \ No newline at end of file diff --git a/src/pages/GraphPage.tsx b/src/pages/GraphPage.tsx new file mode 100644 index 0000000..228c543 --- /dev/null +++ b/src/pages/GraphPage.tsx @@ -0,0 +1,29 @@ +import NetworkGraph from "../components/Graph/NetworkGraph"; + +export default function GraphPage() { + const repos = JSON.parse(localStorage.getItem("repos") || "[]"); + + if (!repos.length) { + return ( +
+ No data found
+ Please analyze organizations first. +
+ ); + } + + return ( +
+ +

+ Contributor Collaboration Network +

+ + + + {/*

+ Visualizes relationships between repositories and contributors across multiple organizations. +

*/} +
+ ); +} diff --git a/src/pages/Overview.tsx b/src/pages/Overview.tsx new file mode 100644 index 0000000..9d8d0d7 --- /dev/null +++ b/src/pages/Overview.tsx @@ -0,0 +1,123 @@ +import { useState, useEffect } from "react"; +import OrgInput from "../components/Input/OrgInput"; +import InsightPanel from "../components/Insights/Insightpanel"; +import ActivityChart from "../components/Charts/ActivityChart"; +import { fetchOrgRepos } from "../services/githubService"; +import { mergeRepos } from "../utils/mergeOrgs"; +import { getInsights } from "../utils/insightEngine"; +import HealthScore from "../components/HealthScore"; +import { calculateOrgHealthScore } from "../utils/calculateScore"; +import type { Repo, Insight } from "../types/github"; +// import { generateInsights } from "../utils/insights"; +import TopContributors from "../components/TopContributors"; + +type Props = { + orgInput: string; + setOrgInput: React.Dispatch>; + setOrgLogo: React.Dispatch>; +}; + +export default function Overview({ orgInput, setOrgInput, setOrgLogo }: Props) { + + const [repos, setRepos] = useState([]); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(false); + + const { score, label } = calculateOrgHealthScore(repos); + + // DEFAULT LOAD + useEffect(() => { + const savedRepos = localStorage.getItem("repos"); + const savedInput = localStorage.getItem("orgInput"); + const savedLogo = localStorage.getItem("orgLogo"); + + if (!savedInput) { + handleSubmit("AOSSIE-Org"); // default + return; + } + + if (savedRepos) { + const parsed: Repo[] = JSON.parse(savedRepos); + setRepos(parsed); + setData(getInsights(parsed)); + } + + if (savedInput) { + setOrgInput(savedInput); + } + + if (savedLogo) { + setOrgLogo(savedLogo); // GLOBAL SET + } + + }, []); + + // ANALYZE + const handleSubmit = async (input: string) => { + setLoading(true); + + try { + setOrgInput(input); + + const orgs = input.split(",").map(o => o.trim()).filter(Boolean); + + // FAST LOGO (NO WAIT) + fetch(`https://api.github.com/orgs/${orgs[0]}`) + .then(res => res.json()) + .then(data => { + setOrgLogo(data.avatar_url); + localStorage.setItem("orgLogo", data.avatar_url); + }) + .catch(() => setOrgLogo("")); + + // MULTI ORG DATA + const results = await Promise.all(orgs.map(fetchOrgRepos)); + const merged: Repo[] = mergeRepos(results); + + setRepos(merged); + + localStorage.setItem("repos", JSON.stringify(merged)); + localStorage.setItem("orgInput", input); + + const insights: Insight = getInsights(merged); + setData(insights); + + } catch (e) { + console.error(e); + } + + setLoading(false); + }; + + return ( +
+ + + + {loading &&

Loading...

} + + {data && } + +
+ +
+ + {/* CHART */} + {repos.length > 0 && ( + o.trim()).filter(Boolean)} + /> + )} + + {/* CONTRIBUTORS */} + {repos.length > 0 && ( + + )} + +
+ ); +} \ No newline at end of file diff --git a/src/pages/RepoDetails.tsx b/src/pages/RepoDetails.tsx new file mode 100644 index 0000000..b4f70eb --- /dev/null +++ b/src/pages/RepoDetails.tsx @@ -0,0 +1,182 @@ +import { useLocation } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { + AreaChart, + Area, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer +} from "recharts"; +import { FaCodeBranch, FaGithub } from "react-icons/fa"; +import { IoIosStarOutline } from "react-icons/io"; +import { MdOutlineReportGmailerrorred } from "react-icons/md"; +; + +const TOKEN = import.meta.env.VITE_GITHUB_TOKEN; + +export default function RepoDetails() { + const { state } = useLocation(); + const repo = state; + + const [contributors, setContributors] = useState([]); + const [chartData, setChartData] = useState([]); + + useEffect(() => { + if (!repo) return; + + fetchContributors(); + fetchActivity(); + }, []); + + // CONTRIBUTORS + const fetchContributors = async () => { + const res = await fetch(repo.contributors_url, { + headers: { Authorization: `token ${TOKEN}` } + }); + const data = await res.json(); + setContributors(data.slice(0, 4)); + }; + + // ACTIVITY CHART + const fetchActivity = async () => { + const res = await fetch( + `https://api.github.com/repos/${repo.full_name}/issues?per_page=100`, + { headers: { Authorization: `token ${TOKEN}` } } + ); + + const data = await res.json(); + + const map: any = {}; + + data.forEach((item: any) => { + const d = new Date(item.created_at) + .toISOString() + .split("T")[0]; + + if (!map[d]) map[d] = { date: d, issues: 0 }; + + map[d].issues++; + }); + + const result = Object.values(map); + setChartData(result); + }; + + if (!repo) { + return

No repo data

; + } + + const formatDate = (date: string) => { + const d = new Date(date); + return d.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + }; + + return ( +
+ + {/* TITLE */} +

+ {repo.name} +

+ + {/* STAT CARDS */} +
+ + {/* STARS */} +
+ +
+

Stars

+

{repo.stargazers_count}

+
+
+ + {/* FORKS */} +
+ +
+

Forks

+

{repo.forks_count}

+
+
+ + {/* ISSUES */} +
+ window.open(repo.html_url + "/issues", "_blank") + } + > + +
+

Issues

+

{repo.open_issues_count}

+
+
+ +
+ + {/* AREA CHART */} +
+

Issues Activity

+ + + + + + + + + + +
+ + {/* CONTRIBUTORS */} +
+

Top Contributors

+ +
+ {contributors.map((c: any) => ( + +
window.open(c.html_url, "_blank")} + className="flex items-center gap-3 bg-gray-800 p-3 rounded cursor-pointer hover:bg-gray-700 transition" + > + + +
+

{c.login}

+

+ Contributions: {c.contributions} +

+
+
+ ))} +
+
+ + {/* GITHUB LINK */} + + +
+ ); +} \ No newline at end of file diff --git a/src/pages/Repositories.tsx b/src/pages/Repositories.tsx new file mode 100644 index 0000000..718bf04 --- /dev/null +++ b/src/pages/Repositories.tsx @@ -0,0 +1,24 @@ +import RepoTable from "../components/Tables/RepoTable"; + +export default function Repositories() { + const repos = JSON.parse(localStorage.getItem("repos") || "[]"); + + return ( + <> +
+ {/*

+ Repositories +

*/} + + {!repos.length ? ( +

+ No data found. Please go to Dashboard and analyze org. +

+ ) : ( + + )} +
+ + + ); +} \ No newline at end of file diff --git a/src/services/githubService.ts b/src/services/githubService.ts new file mode 100644 index 0000000..0f2c54e --- /dev/null +++ b/src/services/githubService.ts @@ -0,0 +1,53 @@ +const BASE = "https://api.github.com"; + +const TOKEN = import.meta.env.VITE_GITHUB_TOKEN; + +export const fetchOrgRepos = async (org: string) => { + const res = await fetch(`${BASE}/orgs/${org}/repos?per_page=100`, { + headers: { + Authorization: `token ${TOKEN}` + } + }); + + if (!res.ok) throw new Error("API Error"); + return res.json(); +}; + +export const fetchRepoContributors = async (url: string) => { + const res = await fetch(url, { + headers: { + Authorization: `token ${TOKEN}` + } + }); + + if (!res.ok) throw new Error("Contributor API Error"); + return res.json(); +}; + +export const fetchRepoIssues = async (owner: string, repo: string) => { + const res = await fetch( + `https://api.github.com/repos/${owner}/${repo}/issues?state=all&per_page=100`, + { + headers: { + Authorization: `token ${TOKEN}` + } + } + ); + + if (!res.ok) throw new Error("Issues API Error"); + return res.json(); +}; + +// Fetch PRs of repo +export const fetchRepoPRs = async (owner: string, repo: string) => { + const res = await fetch( + `${BASE}/repos/${owner}/${repo}/pulls?state=all&per_page=100`, + { + headers: { Authorization: `token ${TOKEN}` } + } + ); + + if (!res.ok) return []; + return res.json(); +}; + diff --git a/src/types/github.ts b/src/types/github.ts new file mode 100644 index 0000000..e4bac02 --- /dev/null +++ b/src/types/github.ts @@ -0,0 +1,16 @@ +export interface Repo { + id: number; + name: string; + stargazers_count: number; + forks_count: number; + updated_at: string; +} + +export interface Insight { + totalRepos: number; + totalStars: number; + totalForks: number; + inactivePercent: string; + topRepos : Repo[]; + insights: string[]; +} \ No newline at end of file diff --git a/src/utils/calculateScore.ts b/src/utils/calculateScore.ts new file mode 100644 index 0000000..69a71b6 --- /dev/null +++ b/src/utils/calculateScore.ts @@ -0,0 +1,48 @@ +export const calculateOrgHealthScore = (repos: any[]) => { + if (!repos || repos.length === 0) return { score: 0, label: "No Data" }; + + const totalRepos = repos.length; + + const activeRepos = repos.filter(repo => { + const days = + (Date.now() - new Date(repo.pushed_at).getTime()) / + (1000 * 60 * 60 * 24); + return days < 30; + }).length; + + const avgStars = + repos.reduce((sum, r) => sum + r.stargazers_count, 0) / totalRepos; + + const avgForks = + repos.reduce((sum, r) => sum + r.forks_count, 0) / totalRepos; + + const staleRepos = repos.filter(repo => { + const days = + (Date.now() - new Date(repo.pushed_at).getTime()) / + (1000 * 60 * 60 * 24); + return days > 180; + }).length; + + let score = 0; + + // Activity score (40) + score += (activeRepos / totalRepos) * 40; + + // Popularity score (30) + score += Math.min(avgStars, 100) * 0.3; + + // Engagement score (20) + score += Math.min(avgForks, 50) * 0.4; + + // Penalty (10) + score -= (staleRepos / totalRepos) * 10; + + score = Math.round(score); + + let label = "Poor"; + if (score > 75) label = "Excellent "; + else if (score > 50) label = "Good "; + else if (score > 30) label = "Average "; + + return { score, label }; +}; \ No newline at end of file diff --git a/src/utils/exportCSV.ts b/src/utils/exportCSV.ts new file mode 100644 index 0000000..80b1441 --- /dev/null +++ b/src/utils/exportCSV.ts @@ -0,0 +1,57 @@ +export const exportCSV = (graphData: any) => { + const { nodes, links } = graphData; + + // Repos + const repoRows = nodes + .filter((n: any) => n.type === "repo") + .map((r: any) => + `${r.label},${r.stars || 0},${r.forks || 0},${r.issues || 0}` + ); + + // Contributors + const userRows = nodes + .filter((n: any) => n.type === "user") + .map((u: any) => + `${u.label},${u.contributions || 0}` + ); + + // Connections + const getId = (val: any) => + typeof val === "object" ? val.id : val; + + const linkRows = links.map((l: any) => + `${getId(l.source)},${getId(l.target)},${l.weight}` + ); + + const parsedRows: string[][] = userRows.map((r: string) => r.split(",")); + + const topContributor: string[] | undefined = parsedRows + .sort((a: string[], b: string[]) => Number(b[1]) - Number(a[1]))[0]; + const csvContent = + // REPO SECTION + "=== REPOSITORIES ===\n" + + "Name,Stars,Forks,Issues\n" + + repoRows.join("\n") + + + "\n\n=== CONTRIBUTORS ===\n" + + "Name,Contributions\n" + + userRows.join("\n") + + + "\n\n=== CONNECTIONS ===\n" + + "Contributor,Repository,Weight\n" + + linkRows.join("\n") + + + "\n\n=== INSIGHTS ===\n" + + `Top Contributor,${topContributor?.[0]}\n`; + + // Download + const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); + const url = URL.createObjectURL(blob); + + const a = document.createElement("a"); + a.href = url; + a.download = "github-analysis.csv"; + a.click(); + + URL.revokeObjectURL(url); +}; \ No newline at end of file diff --git a/src/utils/insightEngine.ts b/src/utils/insightEngine.ts new file mode 100644 index 0000000..5ea30c4 --- /dev/null +++ b/src/utils/insightEngine.ts @@ -0,0 +1,90 @@ +import type { Repo, Insight } from "../types/github"; + +export interface InsightResult extends Insight { + insights: string[]; +} + +export const getInsights = (repos: Repo[]): InsightResult => { + const now = new Date(); + + const inactive = repos.filter(r => + (now.getTime() - new Date(r.updated_at).getTime()) > + 90 * 24 * 60 * 60 * 1000 + ); + + const totalStars = repos.reduce((sum, r) => sum + r.stargazers_count, 0); + const totalForks = repos.reduce((s, r) => s + r.forks_count, 0); + + const topRepos = [...repos] + .sort((a, b) => b.stargazers_count - a.stargazers_count) + .slice(0, 5); + + const inactivePercent = (inactive.length / repos.length) * 100; + + // NEW: INSIGHT GENERATION + const insights: string[] = []; + + // Inactive repos insight + if (inactivePercent > 50) { + insights.push("⚠ More than 50% repositories are inactive → maintenance risk"); + } else if (inactivePercent > 30) { + insights.push("⚠ Significant number of repositories are inactive"); + } else { + insights.push("✅ Most repositories are actively maintained"); + } + + // Star concentration insight + if (topRepos.length > 0) { + const topStar = topRepos[0].stargazers_count; + + if (topStar > totalStars * 0.5) { + insights.push("⚡ One repository dominates more than 50% of total stars"); + } else if (topStar > totalStars * 0.3) { + insights.push("⚡ A few repositories dominate the ecosystem"); + } + } + + // Fork vs Star ratio insight + if (totalStars > 0) { + const ratio = totalForks / totalStars; + + if (ratio > 0.6) { + insights.push(" High fork-to-star ratio → strong developer engagement"); + } else if (ratio < 0.2) { + insights.push(" Low fork activity compared to stars"); + } + } + + // Recently active repos insight + const recent = repos.filter(r => + (now.getTime() - new Date(r.updated_at).getTime()) < + 30 * 24 * 60 * 60 * 1000 + ); + + if (recent.length > repos.length * 0.5) { + insights.push(" High recent activity across repositories"); + } else if (recent.length < repos.length * 0.2) { + insights.push(" Low recent activity → possible slowdown"); + } + + // Repo size distribution insight + const lowStarRepos = repos.filter(r => r.stargazers_count < 10); + + if (lowStarRepos.length > repos.length * 0.6) { + insights.push("📦 Majority of repositories have low visibility (<10 stars)"); + } + + // Growth potential insight + if (totalStars > 1000 && inactivePercent < 30) { + insights.push("📈 Organization shows strong growth potential"); + } + + return { + totalRepos: repos.length, + totalStars, + totalForks, + inactivePercent: inactivePercent.toFixed(1), + topRepos, + insights + }; +}; \ No newline at end of file diff --git a/src/utils/mergeOrgs.ts b/src/utils/mergeOrgs.ts new file mode 100644 index 0000000..cb1e0a7 --- /dev/null +++ b/src/utils/mergeOrgs.ts @@ -0,0 +1,13 @@ +import type { Repo } from "../types/github"; + +export const mergeRepos = (allRepos: Repo[][]): Repo[] => { + const map = new Map(); + + allRepos.flat().forEach(repo => { + if (!map.has(repo.id)) { + map.set(repo.id, repo); + } + }); + + return Array.from(map.values()); +}; \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 8b0f57b..2d12e47 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,9 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ tailwindcss(), + react()], })