diff --git a/graphviz/frontend/.parcelrc b/graphviz/frontend/.parcelrc new file mode 100644 index 0000000..1f3df89 --- /dev/null +++ b/graphviz/frontend/.parcelrc @@ -0,0 +1,4 @@ +{ + "extends": ["@parcel/config-default"], + "reporters": ["...", "parcel-reporter-static-files-copy"] +} diff --git a/graphviz/frontend/.postcssrc b/graphviz/frontend/.postcssrc new file mode 100644 index 0000000..72f908d --- /dev/null +++ b/graphviz/frontend/.postcssrc @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} \ No newline at end of file diff --git a/graphviz/frontend/package-lock.json b/graphviz/frontend/package-lock.json index bfdf995..59ea348 100644 --- a/graphviz/frontend/package-lock.json +++ b/graphviz/frontend/package-lock.json @@ -8,14 +8,74 @@ "name": "parcel-react-client-starter", "version": "0.0.0", "dependencies": { + "@tailwindcss/postcss": "^4.2.4", "react": "^19.2.5", "react-dom": "^19.2.5", - "react-force-graph-2d": "^1.29.1" + "react-force-graph-2d": "^1.29.1", + "tailwindcss": "^4.2.4" }, "devDependencies": { "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "parcel": "^2.14.0" + "parcel": "^2.14.0", + "parcel-reporter-static-files-copy": "^1.5.3" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@lezer/common": { @@ -2173,6 +2233,262 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@tailwindcss/node": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", + "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", + "license": "MIT", + "dependencies": { + "@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.4" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", + "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-x64": "4.2.4", + "@tailwindcss/oxide-freebsd-x64": "4.2.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-x64-musl": "4.2.4", + "@tailwindcss/oxide-wasm32-wasi": "4.2.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", + "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", + "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", + "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", + "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", + "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", + "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", + "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", + "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", + "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", + "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", + "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.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", + "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", + "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.4.tgz", + "integrity": "sha512-wgAVj6nUWAolAu8YFvzT2cTBIElWHkjZwFYovF+xsqKsW2ADxM/X2opxj5NsF/qVccAOjRNe8X2IdPzMsWyHTg==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.4", + "@tailwindcss/oxide": "4.2.4", + "postcss": "^8.5.6", + "tailwindcss": "4.2.4" + } + }, "node_modules/@tweenjs/tween.js": { "version": "25.0.0", "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", @@ -2663,6 +2979,19 @@ "dev": true, "license": "ISC" }, + "node_modules/enhanced-resolve": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2739,6 +3068,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", @@ -2799,6 +3134,15 @@ "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", @@ -2834,7 +3178,6 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -2867,7 +3210,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2888,7 +3230,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2909,7 +3250,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2930,7 +3270,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2951,7 +3290,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2972,7 +3310,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2993,7 +3330,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3014,7 +3350,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3035,7 +3370,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3056,7 +3390,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3077,7 +3410,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3095,7 +3427,6 @@ "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" @@ -3152,6 +3483,15 @@ "loose-envify": "cli.js" } }, + "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/msgpackr": { "version": "1.11.9", "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.9.tgz", @@ -3212,6 +3552,24 @@ "node-gyp-build-optional-packages-test": "build-test.js" } }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -3308,11 +3666,23 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/parcel-reporter-static-files-copy": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/parcel-reporter-static-files-copy/-/parcel-reporter-static-files-copy-1.5.3.tgz", + "integrity": "sha512-Ukq2SyJYn3GFIPCLamXuQ+2t+0j54llujjOUoRjtmVvfsuGnJDEpMznADeIoKuQDvy0jpxtWzWkQvxqI/j+U4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "^2.0.0-beta.1" + }, + "engines": { + "parcel": "^2.0.0-beta.1" + } + }, "node_modules/picocolors": { "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": { @@ -3328,6 +3698,34 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postcss": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -3472,6 +3870,15 @@ "node": ">=10" } }, + "node_modules/source-map-js": { + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3485,6 +3892,25 @@ "node": ">=8" } }, + "node_modules/tailwindcss": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", + "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/term-size": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", @@ -3508,7 +3934,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/type-fest": { diff --git a/graphviz/frontend/package.json b/graphviz/frontend/package.json index 3f0e148..a73686f 100644 --- a/graphviz/frontend/package.json +++ b/graphviz/frontend/package.json @@ -8,13 +8,16 @@ "build": "parcel build" }, "dependencies": { + "@tailwindcss/postcss": "^4.2.4", "react": "^19.2.5", "react-dom": "^19.2.5", - "react-force-graph-2d": "^1.29.1" + "react-force-graph-2d": "^1.29.1", + "tailwindcss": "^4.2.4" }, "devDependencies": { "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "parcel": "^2.14.0" + "parcel": "^2.14.0", + "parcel-reporter-static-files-copy": "^1.5.3" } } diff --git a/graphviz/frontend/src/App.css b/graphviz/frontend/src/App.css deleted file mode 100644 index b9e66b6..0000000 --- a/graphviz/frontend/src/App.css +++ /dev/null @@ -1,8 +0,0 @@ -html { - color-scheme: light dark; - font-family: system-ui; - display: flex; - align-items: center; - justify-content: center; - height: 100%; -} diff --git a/graphviz/frontend/src/AppRouter.tsx b/graphviz/frontend/src/AppRouter.tsx deleted file mode 100644 index f474c2a..0000000 --- a/graphviz/frontend/src/AppRouter.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect, useState } from "react"; -import { VizSmallConnected } from "./VizSmallConnected"; -import { VizTimeFilter } from "./VizTimeFilter"; - -function Home() { - return ( -
-

Will Jeynes - LLMs for Disinformation Analysis - Graph Visualisations

-

Default

-

Time-Filter

-
- ); -} - -export function AppRouter() { - const [route, setRoute] = useState(() => window.location.hash); - - useEffect(() => { - const onHashChange = () => { - setRoute(window.location.hash); - }; - - window.addEventListener("hashchange", onHashChange); - return () => window.removeEventListener("hashchange", onHashChange); - }, []); - console.log(route) - if (route === "#small") return ; - if (route === "#time") return ; - return ; -} \ No newline at end of file diff --git a/graphviz/frontend/src/Home.tsx b/graphviz/frontend/src/Home.tsx new file mode 100644 index 0000000..06dff49 --- /dev/null +++ b/graphviz/frontend/src/Home.tsx @@ -0,0 +1,86 @@ +export function Home() { + return ( +
+
+
+

LLMs for Disinformation Analysis

+

Dataset Visualizations

+ +

By Will Jeynes

+
+
+ +
+
+ +

Default View

+
+ + + +

+ A filtered collection of the whole dataset, containing only reasonably sized components +

+

+ A great introduction to the dataset on a curated set of examples +

+
+ +
+ +

Time Filtered

+
+ + + +

+ A visualisation showing only the largest component, normally too large to be understandable +

+

+ Configurable, scrubber date filter allows migration to be seen over time +

+
+
+ +
+

+ Project Description +

+

+ Description coming soon +

+ +

+ Sources +

+ + + + + +
+
+
+ ); +} \ No newline at end of file diff --git a/graphviz/frontend/src/VizSmallConnected.tsx b/graphviz/frontend/src/VizSmallConnected.tsx index 60f4eb0..99b8740 100644 --- a/graphviz/frontend/src/VizSmallConnected.tsx +++ b/graphviz/frontend/src/VizSmallConnected.tsx @@ -4,6 +4,10 @@ import * as d3 from "d3-force-3d"; import data from "./data.json"; import titlesData from "./titles.json"; +import HomeButton from "./utils/HomeButton"; +import { FloatingPanel } from "./utils/FloatingPanel"; +import { DetailsPanel } from "./utils/DetailsPanel"; +import { FloatingPanelStack } from "./utils/FloatingPanelStack"; function drawRoundedRect(ctx, x, y, width, height, radius) { const r = Math.min(radius, width / 2, height / 2); @@ -33,6 +37,7 @@ function buildGraph(data) { id: cluster.cluster_id, label: titleMap.get(cluster.cluster_id) || cluster.title || "Unnamed Claim Cluster", type: "claim_cluster", + type_nice: "Claim", members: cluster.members }); }); @@ -42,6 +47,7 @@ function buildGraph(data) { id: cluster.cluster_id, label: titleMap.get(cluster.cluster_id) || cluster.title || "Unnamed Event Cluster", type: "event_cluster", + type_nice: "Event", members: cluster.members }); }); @@ -131,7 +137,7 @@ export function VizSmallConnected() { d3.forceManyBody().strength(-10000) ); - + // Link distance fgRef.current.d3Force( @@ -151,7 +157,7 @@ export function VizSmallConnected() { fgRef.current.d3ReheatSimulation(); }, [graphData]); - useEffect(() => { + useEffect(() => { if (fgRef.current) { fgRef.current.zoom(0.01, 0); } @@ -159,6 +165,7 @@ export function VizSmallConnected() { return (
+ - -
-

Go Home

-

Config

+ + + +

Trigger Event Cluster

+

Claim Cluster

+
+ @@ -242,35 +242,12 @@ export function VizSmallConnected() { value={minGraphSize} onChange={(e) => setMinGraphSize(Number(e.target.value))} /> + + +
-

Details

- {selectedNode ? ( -
-

Title: {selectedNode.label}

- {selectedNode.members && ( -
-

Members:

-
    - {selectedNode.members.map((m) => { - const memberData = - data.claims.find((c) => c.id === m) || - data.events.find((e) => e.id === m); - return ( -
  • - {memberData ? memberData.text : m} -
  • - ); - })} -
-
- )} -
- ) : ( -

Click a node to see details

- )} -
); } \ No newline at end of file diff --git a/graphviz/frontend/src/index.css b/graphviz/frontend/src/index.css new file mode 100644 index 0000000..a461c50 --- /dev/null +++ b/graphviz/frontend/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; \ No newline at end of file diff --git a/graphviz/frontend/src/index.html b/graphviz/frontend/src/index.html index 7ef4367..d87e486 100644 --- a/graphviz/frontend/src/index.html +++ b/graphviz/frontend/src/index.html @@ -3,6 +3,7 @@ + Parcel React App diff --git a/graphviz/frontend/src/index.tsx b/graphviz/frontend/src/index.tsx index aefc1f6..efd88cf 100644 --- a/graphviz/frontend/src/index.tsx +++ b/graphviz/frontend/src/index.tsx @@ -1,6 +1,26 @@ import { createRoot } from "react-dom/client"; import { StrictMode } from "react"; -import { AppRouter } from "./AppRouter"; +import { useEffect, useState } from "react"; +import { VizSmallConnected } from "./VizSmallConnected"; +import { VizTimeFilter } from "./VizTimeFilter"; +import { Home } from "./Home"; + +export function AppRouter() { + const [route, setRoute] = useState(() => window.location.hash); + + useEffect(() => { + const onHashChange = () => { + setRoute(window.location.hash); + }; + + window.addEventListener("hashchange", onHashChange); + return () => window.removeEventListener("hashchange", onHashChange); + }, []); + + if (route === "#small") return ; + if (route === "#time") return ; + return ; +} let container = document.getElementById("app")!; let root = createRoot(container); diff --git a/graphviz/frontend/src/utils/DetailsPanel.tsx b/graphviz/frontend/src/utils/DetailsPanel.tsx new file mode 100644 index 0000000..0894984 --- /dev/null +++ b/graphviz/frontend/src/utils/DetailsPanel.tsx @@ -0,0 +1,43 @@ +import { FloatingPanel } from "./FloatingPanel"; + +export function DetailsPanel({ selectedNode, data }) { + return ( + + + {selectedNode ? ( +
+

+ Type: {selectedNode.type_nice} Cluster +

+ +

+ Title: {selectedNode.label} +

+ + {selectedNode.members && ( +
+

Members:

+
    + {selectedNode.members.map((m) => { + const memberData = + data.claims.find((c) => c.id === m) || + data.events.find((e) => e.id === m); + + return ( +
  • + {memberData ? memberData.text : m} +
  • + ); + })} +
+
+ )} +
+ ) : ( +

+ Click a node to see details +

+ )} +
+ ); +} \ No newline at end of file diff --git a/graphviz/frontend/src/utils/FloatingPanel.tsx b/graphviz/frontend/src/utils/FloatingPanel.tsx new file mode 100644 index 0000000..11566d9 --- /dev/null +++ b/graphviz/frontend/src/utils/FloatingPanel.tsx @@ -0,0 +1,37 @@ +import { useState } from "react"; + +export function FloatingPanel({ + title, + children, + defaultOpen = true, +}) { + const [open, setOpen] = useState(defaultOpen); + + return ( +
+ + + +
+ {children} +
+
+ ); +} \ No newline at end of file diff --git a/graphviz/frontend/src/utils/FloatingPanelStack.tsx b/graphviz/frontend/src/utils/FloatingPanelStack.tsx new file mode 100644 index 0000000..6a9c9d1 --- /dev/null +++ b/graphviz/frontend/src/utils/FloatingPanelStack.tsx @@ -0,0 +1,7 @@ +export function FloatingPanelStack({ children }) { + return ( +
+ {children} +
+ ); +} \ No newline at end of file diff --git a/graphviz/frontend/src/utils/HomeButton.tsx b/graphviz/frontend/src/utils/HomeButton.tsx new file mode 100644 index 0000000..e399782 --- /dev/null +++ b/graphviz/frontend/src/utils/HomeButton.tsx @@ -0,0 +1,23 @@ +export default function HomeButton() { + return ( + + + + + + ); +} \ No newline at end of file diff --git a/graphviz/frontend/static/small.png b/graphviz/frontend/static/small.png new file mode 100644 index 0000000..fe20612 Binary files /dev/null and b/graphviz/frontend/static/small.png differ diff --git a/graphviz/frontend/static/time.png b/graphviz/frontend/static/time.png new file mode 100644 index 0000000..ca5fc8a Binary files /dev/null and b/graphviz/frontend/static/time.png differ