diff --git a/drone-frontend/.gitignore b/drone-frontend/.gitignore
new file mode 100644
index 0000000..f6a0056
--- /dev/null
+++ b/drone-frontend/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+dist
+.svelte-kit
+.DS_Store
diff --git a/drone-frontend/bun.lock b/drone-frontend/bun.lock
new file mode 100644
index 0000000..11cf777
--- /dev/null
+++ b/drone-frontend/bun.lock
@@ -0,0 +1,286 @@
+{
+ "lockfileVersion": 1,
+ "configVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "drone-frontend",
+ "dependencies": {
+ "leaflet": "^1.9.4",
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^3.0.2",
+ "@tailwindcss/postcss": "^4.1.17",
+ "autoprefixer": "^10.4.22",
+ "postcss": "^8.5.6",
+ "svelte": "^4.2.18",
+ "tailwindcss": "^4.1.17",
+ "vite": "^5.4.8",
+ },
+ },
+ },
+ "packages": {
+ "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
+
+ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
+
+ "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="],
+
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="],
+
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="],
+
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="],
+
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="],
+
+ "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@3.1.2", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.10", "svelte-hmr": "^0.16.0", "vitefu": "^0.2.5" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.0" } }, "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA=="],
+
+ "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@2.1.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.0" } }, "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg=="],
+
+ "@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="],
+
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="],
+
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="],
+
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="],
+
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="],
+
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="],
+
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="],
+
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="],
+
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="],
+
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="],
+
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="],
+
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="],
+
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="],
+
+ "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", "tailwindcss": "4.1.17" } }, "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw=="],
+
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+
+ "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
+
+ "autoprefixer": ["autoprefixer@10.4.22", "", { "dependencies": { "browserslist": "^4.27.0", "caniuse-lite": "^1.0.30001754", "fraction.js": "^5.3.4", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg=="],
+
+ "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
+
+ "baseline-browser-mapping": ["baseline-browser-mapping@2.9.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-Mh++g+2LPfzZToywfE1BUzvZbfOY52Nil0rn9H1CPC5DJ7fX+Vir7nToBeoiSbB1zTNeGYbELEvJESujgGrzXw=="],
+
+ "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
+
+ "caniuse-lite": ["caniuse-lite@1.0.30001759", "", {}, "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw=="],
+
+ "code-red": ["code-red@1.0.4", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1", "acorn": "^8.10.0", "estree-walker": "^3.0.3", "periscopic": "^3.1.0" } }, "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw=="],
+
+ "css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
+
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
+
+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
+
+ "electron-to-chromium": ["electron-to-chromium@1.5.264", "", {}, "sha512-1tEf0nLgltC3iy9wtlYDlQDc5Rg9lEKVjEmIHJ21rI9OcqkvD45K1oyNIRA4rR1z3LgJ7KeGzEBojVcV6m4qjA=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
+
+ "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
+
+ "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
+
+ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
+
+ "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
+
+ "leaflet": ["leaflet@1.9.4", "", {}, "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="],
+
+ "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
+
+ "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
+
+ "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
+
+ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
+
+ "mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
+
+ "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
+
+ "periscopic": ["periscopic@3.1.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^3.0.0", "is-reference": "^3.0.0" } }, "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+
+ "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
+
+ "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "svelte": ["svelte@4.2.20", "", { "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", "@jridgewell/trace-mapping": "^0.3.18", "@types/estree": "^1.0.1", "acorn": "^8.9.0", "aria-query": "^5.3.0", "axobject-query": "^4.0.0", "code-red": "^1.0.3", "css-tree": "^2.3.1", "estree-walker": "^3.0.3", "is-reference": "^3.0.1", "locate-character": "^3.0.0", "magic-string": "^0.30.4", "periscopic": "^3.1.0" } }, "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q=="],
+
+ "svelte-hmr": ["svelte-hmr@0.16.0", "", { "peerDependencies": { "svelte": "^3.19.0 || ^4.0.0" } }, "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA=="],
+
+ "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="],
+
+ "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
+
+ "update-browserslist-db": ["update-browserslist-db@1.2.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="],
+
+ "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
+
+ "vitefu": ["vitefu@0.2.5", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["vite"] }, "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+ }
+}
diff --git a/drone-frontend/index.html b/drone-frontend/index.html
new file mode 100644
index 0000000..195f07a
--- /dev/null
+++ b/drone-frontend/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Drone Path Explorer
+
+
+
+
+
+
+
diff --git a/drone-frontend/package.json b/drone-frontend/package.json
new file mode 100644
index 0000000..5b385c7
--- /dev/null
+++ b/drone-frontend/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "drone-frontend",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "leaflet": "^1.9.4"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^3.0.2",
+ "@tailwindcss/postcss": "^4.1.17",
+ "autoprefixer": "^10.4.22",
+ "postcss": "^8.5.6",
+ "svelte": "^4.2.18",
+ "tailwindcss": "^4.1.17",
+ "vite": "^5.4.8"
+ }
+}
diff --git a/drone-frontend/postcss.config.js b/drone-frontend/postcss.config.js
new file mode 100644
index 0000000..4614d74
--- /dev/null
+++ b/drone-frontend/postcss.config.js
@@ -0,0 +1,7 @@
+/** @type {import('postcss-load-config').Config} */
+export default {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ autoprefixer: {},
+ },
+};
diff --git a/drone-frontend/public/favicon.svg b/drone-frontend/public/favicon.svg
new file mode 100644
index 0000000..c5c3832
--- /dev/null
+++ b/drone-frontend/public/favicon.svg
@@ -0,0 +1,11 @@
+
diff --git a/drone-frontend/src/App.svelte b/drone-frontend/src/App.svelte
new file mode 100644
index 0000000..298e0b6
--- /dev/null
+++ b/drone-frontend/src/App.svelte
@@ -0,0 +1,400 @@
+
+
+
+
+
+
+
+
+
+
+ (sidebarOpen = false)}
+ on:request={requestPath}
+ on:loadSample={loadSample}
+ />
+
+
+
+
diff --git a/drone-frontend/src/app.css b/drone-frontend/src/app.css
new file mode 100644
index 0000000..0f74a24
--- /dev/null
+++ b/drone-frontend/src/app.css
@@ -0,0 +1,58 @@
+@import "leaflet/dist/leaflet.css";
+@import "tailwindcss";
+
+@layer base {
+ :root {
+ /* Light theme palette for drone paths - Distinct Colors */
+ --p-1: theme("colors.green.600");
+ --p-2: theme("colors.yellow.600");
+ --p-3: theme("colors.sky.500");
+ --p-4: theme("colors.pink.600");
+ --p-5: theme("colors.violet.600");
+ --p-6: theme("colors.red.600");
+ }
+
+ .dark {
+ /* Dark theme palette for drone paths - Neon Colors */
+ --p-1: #00ff00;
+ --p-2: #ffff00;
+ --p-3: #00ffff;
+ --p-4: #ff00ff;
+ --p-5: #bd00ff;
+ --p-6: #ff4d4d;
+ }
+}
+
+.leaflet-container {
+ background: #111 !important;
+}
+
+.drone-tooltip {
+ @apply bg-gray-800 text-white p-2 rounded-md border-gray-700 border;
+}
+
+/* Custom Scrollbar */
+.custom-scrollbar::-webkit-scrollbar {
+ width: 6px;
+}
+.custom-scrollbar::-webkit-scrollbar-track {
+ background: transparent;
+}
+.custom-scrollbar::-webkit-scrollbar-thumb {
+ background-color: rgba(148, 163, 184, 0.3); /* Slate 400 with opacity */
+ border-radius: 20px;
+}
+.custom-scrollbar:hover::-webkit-scrollbar-thumb {
+ background-color: rgba(148, 163, 184, 0.5);
+}
+
+/* Drone Popup Styles */
+.drone-popup .leaflet-popup-content-wrapper {
+ @apply bg-slate-900/90 backdrop-blur-md border border-slate-700 text-slate-100 rounded-lg p-0 shadow-xl;
+}
+.drone-popup .leaflet-popup-tip {
+ @apply bg-slate-900/90 border-slate-700;
+}
+.drone-popup .leaflet-popup-content {
+ margin: 12px;
+}
\ No newline at end of file
diff --git a/drone-frontend/src/components/PlaybackControls.svelte b/drone-frontend/src/components/PlaybackControls.svelte
new file mode 100644
index 0000000..3c0adf6
--- /dev/null
+++ b/drone-frontend/src/components/PlaybackControls.svelte
@@ -0,0 +1,164 @@
+
+
+
+
+
+ Mission Time
+ {wallClock.split("T")[1].slice(0, 8)}
+
+
+ T+ Elapsed
+ {formatDuration(playbackSeconds)}
+
+
+ Total Cost
+ {totalCost ?? "—"}
+
+
+ Active Drones
+ {activeDrones ?? 0}
+
+
+
+
+
+
+
dispatch('seek')}
+ class="w-full h-2 bg-slate-200 dark:bg-slate-700 rounded-full appearance-none"
+ style="--thumb-color: {$theme === 'dark'
+ ? '#f1f5f9'
+ : '#1e293b'};"
+ />
+
+
+ {Math.round((tick / (totalSteps - 1 || 1)) * 100)}%
+
+
+
+
+
+ {snapshotStatus || "Black Box Offline"}
+
+
+
+
+ {#if snapshot.length > 0}
+
+ {#each snapshot.slice(0, 3) as event}
+
+ Drone {event.drone_id} log found
+
+ {/each}
+ {#if snapshot.length > 3}
+
+ +{snapshot.length - 3} more
+
+ {/if}
+
+ {/if}
+
+
+
diff --git a/drone-frontend/src/components/Sidebar.svelte b/drone-frontend/src/components/Sidebar.svelte
new file mode 100644
index 0000000..dd1b7c2
--- /dev/null
+++ b/drone-frontend/src/components/Sidebar.svelte
@@ -0,0 +1,209 @@
+
+
+
\ No newline at end of file
diff --git a/drone-frontend/src/main.js b/drone-frontend/src/main.js
new file mode 100644
index 0000000..09f41e9
--- /dev/null
+++ b/drone-frontend/src/main.js
@@ -0,0 +1,8 @@
+import './app.css';
+import App from './App.svelte';
+
+const app = new App({
+ target: document.getElementById('app')
+});
+
+export default app;
diff --git a/drone-frontend/src/sampleData.js b/drone-frontend/src/sampleData.js
new file mode 100644
index 0000000..1e729d2
--- /dev/null
+++ b/drone-frontend/src/sampleData.js
@@ -0,0 +1,1336 @@
+// Data from Bruno Test Query
+export const STEP_SECONDS = 30;
+
+export const fallbackBounds = {
+ minLng: -3.19,
+ maxLng: -3.17,
+ minLat: 55.94,
+ maxLat: 55.99,
+};
+
+export const defaultDispatch = [
+ {
+ id: 501,
+ date: "2025-01-06",
+ time: "12:00:00",
+ requirements: { capacity: 0.5, cooling: false, heating: true, maxCost: 50 },
+ delivery: { lng: -3.1852, lat: 55.9451 },
+ },
+ {
+ id: 502,
+ date: "2025-01-06",
+ time: "12:15:00",
+ requirements: {
+ capacity: 1.5,
+ cooling: true,
+ heating: false,
+ maxCost: 120,
+ },
+ delivery: { lng: -3.1776, lat: 55.9498 },
+ },
+];
+
+// export const samplePathResponse = {
+// totalCost: 85.5,
+// totalMoves: 22,
+// dronePaths: [
+// {
+// droneId: 101,
+// deliveries: [
+// {
+// deliveryId: 501,
+// flightPath: [
+// { lng: -3.186358, lat: 55.944681 },
+// { lng: -3.1856, lat: 55.9448 },
+// { lng: -3.1845, lat: 55.9452 },
+// { lng: -3.1842, lat: 55.9456 },
+// { lng: -3.1851, lat: 55.9454 },
+// { lng: -3.186358, lat: 55.944681 },
+// ],
+// },
+// ],
+// },
+// {
+// droneId: 202,
+// deliveries: [
+// {
+// deliveryId: 502,
+// flightPath: [
+// { lng: -3.177326, lat: 55.981186 },
+// { lng: -3.1771, lat: 55.9804 },
+// { lng: -3.1762, lat: 55.9796 },
+// { lng: -3.1769, lat: 55.9788 },
+// { lng: -3.177326, lat: 55.981186 },
+// ],
+// },
+// ],
+// },
+// ],
+// };
+
+export const samplePathResponse = {
+ totalCost: 18.43,
+ totalMoves: 306,
+ dronePaths: [
+ {
+ droneId: 1,
+ deliveries: [
+ {
+ deliveryId: 123,
+ flightPath: [
+ {
+ lng: -3.1863580788986368,
+ lat: 55.94468066708487,
+ },
+ {
+ lng: -3.186464144915815,
+ lat: 55.94478673310204,
+ },
+ {
+ lng: -3.186570210932993,
+ lat: 55.944892799119216,
+ },
+ {
+ lng: -3.186676276950171,
+ lat: 55.94499886513639,
+ },
+ {
+ lng: -3.186782342967349,
+ lat: 55.945104931153566,
+ },
+ {
+ lng: -3.1868884089845273,
+ lat: 55.94521099717074,
+ },
+ {
+ lng: -3.1869944750017054,
+ lat: 55.945317063187915,
+ },
+ {
+ lng: -3.1871005410188835,
+ lat: 55.94542312920509,
+ },
+ {
+ lng: -3.1872066070360616,
+ lat: 55.945529195222264,
+ },
+ {
+ lng: -3.1873126730532397,
+ lat: 55.94563526123944,
+ },
+ {
+ lng: -3.187418739070418,
+ lat: 55.94574132725661,
+ },
+ {
+ lng: -3.187524805087596,
+ lat: 55.94584739327379,
+ },
+ {
+ lng: -3.187630871104774,
+ lat: 55.94595345929096,
+ },
+ {
+ lng: -3.187736937121952,
+ lat: 55.94605952530814,
+ },
+ {
+ lng: -3.1878430031391303,
+ lat: 55.94616559132531,
+ },
+ {
+ lng: -3.1879490691563084,
+ lat: 55.946271657342486,
+ },
+ {
+ lng: -3.1880551351734865,
+ lat: 55.94637772335966,
+ },
+ {
+ lng: -3.1881612011906646,
+ lat: 55.946483789376835,
+ },
+ {
+ lng: -3.1882672672078427,
+ lat: 55.94658985539401,
+ },
+ {
+ lng: -3.188373333225021,
+ lat: 55.946695921411184,
+ },
+ {
+ lng: -3.188479399242199,
+ lat: 55.94680198742836,
+ },
+ {
+ lng: -3.188585465259377,
+ lat: 55.94690805344553,
+ },
+ {
+ lng: -3.188691531276555,
+ lat: 55.94701411946271,
+ },
+ {
+ lng: -3.1887975972937332,
+ lat: 55.94712018547988,
+ },
+ {
+ lng: -3.1889036633109114,
+ lat: 55.94722625149706,
+ },
+ {
+ lng: -3.1890097293280895,
+ lat: 55.94733231751423,
+ },
+ {
+ lng: -3.189067131842944,
+ lat: 55.947470899444106,
+ },
+ {
+ lng: -3.1891731978601223,
+ lat: 55.94757696546128,
+ },
+ {
+ lng: -3.189230600374977,
+ lat: 55.947715547391155,
+ },
+ {
+ lng: -3.189336666392155,
+ lat: 55.94782161340833,
+ },
+ {
+ lng: -3.18939406890701,
+ lat: 55.947960195338204,
+ },
+ {
+ lng: -3.189500134924188,
+ lat: 55.94806626135538,
+ },
+ {
+ lng: -3.189557537439043,
+ lat: 55.948204843285254,
+ },
+ {
+ lng: -3.189663603456221,
+ lat: 55.94831090930243,
+ },
+ {
+ lng: -3.1897210059710757,
+ lat: 55.9484494912323,
+ },
+ {
+ lng: -3.1898270719882538,
+ lat: 55.94855555724948,
+ },
+ {
+ lng: -3.1898844745031085,
+ lat: 55.94869413917935,
+ },
+ {
+ lng: -3.1899905405202866,
+ lat: 55.94880020519653,
+ },
+ {
+ lng: -3.1900479430351414,
+ lat: 55.9489387871264,
+ },
+ {
+ lng: -3.1901540090523195,
+ lat: 55.949044853143576,
+ },
+ {
+ lng: -3.1902114115671742,
+ lat: 55.94918343507345,
+ },
+ {
+ lng: -3.1903174775843524,
+ lat: 55.949289501090625,
+ },
+ {
+ lng: -3.190374880099207,
+ lat: 55.9494280830205,
+ },
+ {
+ lng: -3.190480946116385,
+ lat: 55.949534149037675,
+ },
+ {
+ lng: -3.19053834863124,
+ lat: 55.94967273096755,
+ },
+ {
+ lng: -3.190644414648418,
+ lat: 55.949778796984724,
+ },
+ {
+ lng: -3.190701817163273,
+ lat: 55.9499173789146,
+ },
+ {
+ lng: -3.190807883180451,
+ lat: 55.95002344493177,
+ },
+ {
+ lng: -3.1908652856953057,
+ lat: 55.95016202686165,
+ },
+ {
+ lng: -3.190971351712484,
+ lat: 55.95026809287882,
+ },
+ {
+ lng: -3.1910287542273386,
+ lat: 55.9504066748087,
+ },
+ {
+ lng: -3.1911348202445167,
+ lat: 55.95051274082587,
+ },
+ {
+ lng: -3.1911922227593714,
+ lat: 55.950651322755746,
+ },
+ {
+ lng: -3.1912982887765495,
+ lat: 55.95075738877292,
+ },
+ {
+ lng: -3.1913556912914043,
+ lat: 55.950895970702796,
+ },
+ {
+ lng: -3.1914617573085824,
+ lat: 55.95100203671997,
+ },
+ {
+ lng: -3.191499773625054,
+ lat: 55.9510924649214,
+ },
+ {
+ lng: -3.191499773625054,
+ lat: 55.9510924649214,
+ },
+ {
+ lng: -3.191393707607876,
+ lat: 55.95098639890423,
+ },
+ {
+ lng: -3.191287641590698,
+ lat: 55.95088033288705,
+ },
+ {
+ lng: -3.19118157557352,
+ lat: 55.95077426686988,
+ },
+ {
+ lng: -3.1910755095563417,
+ lat: 55.950668200852704,
+ },
+ {
+ lng: -3.1909694435391636,
+ lat: 55.95056213483553,
+ },
+ {
+ lng: -3.1908633775219855,
+ lat: 55.950456068818355,
+ },
+ {
+ lng: -3.1907573115048073,
+ lat: 55.95035000280118,
+ },
+ {
+ lng: -3.1906512454876292,
+ lat: 55.950243936784005,
+ },
+ {
+ lng: -3.190545179470451,
+ lat: 55.95013787076683,
+ },
+ {
+ lng: -3.190439113453273,
+ lat: 55.950031804749656,
+ },
+ {
+ lng: -3.190333047436095,
+ lat: 55.94992573873248,
+ },
+ {
+ lng: -3.190226981418917,
+ lat: 55.94981967271531,
+ },
+ {
+ lng: -3.1901209154017387,
+ lat: 55.94971360669813,
+ },
+ {
+ lng: -3.1900148493845606,
+ lat: 55.94960754068096,
+ },
+ {
+ lng: -3.1899087833673825,
+ lat: 55.94950147466378,
+ },
+ {
+ lng: -3.1898027173502044,
+ lat: 55.94939540864661,
+ },
+ {
+ lng: -3.1896966513330263,
+ lat: 55.949289342629434,
+ },
+ {
+ lng: -3.189590585315848,
+ lat: 55.94918327661226,
+ },
+ {
+ lng: -3.18948451929867,
+ lat: 55.949077210595085,
+ },
+ {
+ lng: -3.189378453281492,
+ lat: 55.94897114457791,
+ },
+ {
+ lng: -3.189272387264314,
+ lat: 55.948865078560736,
+ },
+ {
+ lng: -3.1891663212471357,
+ lat: 55.94875901254356,
+ },
+ {
+ lng: -3.1890602552299576,
+ lat: 55.94865294652639,
+ },
+ {
+ lng: -3.1889541892127795,
+ lat: 55.94854688050921,
+ },
+ {
+ lng: -3.1888481231956014,
+ lat: 55.94844081449204,
+ },
+ {
+ lng: -3.1887907206807466,
+ lat: 55.94830223256216,
+ },
+ {
+ lng: -3.1886846546635685,
+ lat: 55.94819616654499,
+ },
+ {
+ lng: -3.188627252148714,
+ lat: 55.948057584615114,
+ },
+ {
+ lng: -3.1885211861315357,
+ lat: 55.94795151859794,
+ },
+ {
+ lng: -3.188463783616681,
+ lat: 55.947812936668065,
+ },
+ {
+ lng: -3.188357717599503,
+ lat: 55.94770687065089,
+ },
+ {
+ lng: -3.188300315084648,
+ lat: 55.947568288721016,
+ },
+ {
+ lng: -3.18819424906747,
+ lat: 55.94746222270384,
+ },
+ {
+ lng: -3.188136846552615,
+ lat: 55.947323640773966,
+ },
+ {
+ lng: -3.188030780535437,
+ lat: 55.94721757475679,
+ },
+ {
+ lng: -3.1879733780205823,
+ lat: 55.94707899282692,
+ },
+ {
+ lng: -3.1878673120034042,
+ lat: 55.94697292680974,
+ },
+ {
+ lng: -3.1878099094885495,
+ lat: 55.94683434487987,
+ },
+ {
+ lng: -3.1877038434713714,
+ lat: 55.94672827886269,
+ },
+ {
+ lng: -3.1876464409565166,
+ lat: 55.94658969693282,
+ },
+ {
+ lng: -3.1875403749393385,
+ lat: 55.946483630915644,
+ },
+ {
+ lng: -3.1874829724244838,
+ lat: 55.94634504898577,
+ },
+ {
+ lng: -3.1873769064073056,
+ lat: 55.946238982968595,
+ },
+ {
+ lng: -3.187319503892451,
+ lat: 55.94610040103872,
+ },
+ {
+ lng: -3.187213437875273,
+ lat: 55.945994335021545,
+ },
+ {
+ lng: -3.187156035360418,
+ lat: 55.94585575309167,
+ },
+ {
+ lng: -3.18704996934324,
+ lat: 55.945749687074496,
+ },
+ {
+ lng: -3.186992566828385,
+ lat: 55.94561110514462,
+ },
+ {
+ lng: -3.186886500811207,
+ lat: 55.94550503912745,
+ },
+ {
+ lng: -3.1868290982963523,
+ lat: 55.94536645719757,
+ },
+ {
+ lng: -3.186723032279174,
+ lat: 55.9452603911804,
+ },
+ {
+ lng: -3.1866656297643194,
+ lat: 55.94512180925052,
+ },
+ {
+ lng: -3.1865595637471413,
+ lat: 55.94501574323335,
+ },
+ {
+ lng: -3.1865021612322866,
+ lat: 55.944877161303474,
+ },
+ {
+ lng: -3.1863960952151085,
+ lat: 55.9447710952863,
+ },
+ {
+ lng: -3.1863580788986368,
+ lat: 55.94468066708487,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ droneId: 7,
+ deliveries: [
+ {
+ deliveryId: 456,
+ flightPath: [
+ {
+ lng: -3.17732611501824,
+ lat: 55.981186279333656,
+ },
+ {
+ lng: -3.1774321810354182,
+ lat: 55.98108021331648,
+ },
+ {
+ lng: -3.1775382470525964,
+ lat: 55.98097414729931,
+ },
+ {
+ lng: -3.1776443130697745,
+ lat: 55.98086808128213,
+ },
+ {
+ lng: -3.1777503790869526,
+ lat: 55.98076201526496,
+ },
+ {
+ lng: -3.1778564451041307,
+ lat: 55.98065594924778,
+ },
+ {
+ lng: -3.177962511121309,
+ lat: 55.98054988323061,
+ },
+ {
+ lng: -3.178068577138487,
+ lat: 55.980443817213434,
+ },
+ {
+ lng: -3.178174643155665,
+ lat: 55.98033775119626,
+ },
+ {
+ lng: -3.178280709172843,
+ lat: 55.980231685179085,
+ },
+ {
+ lng: -3.178386775190021,
+ lat: 55.98012561916191,
+ },
+ {
+ lng: -3.1784928412071993,
+ lat: 55.980019553144736,
+ },
+ {
+ lng: -3.1785989072243774,
+ lat: 55.97991348712756,
+ },
+ {
+ lng: -3.1787049732415555,
+ lat: 55.97980742111039,
+ },
+ {
+ lng: -3.1788110392587337,
+ lat: 55.97970135509321,
+ },
+ {
+ lng: -3.1789171052759118,
+ lat: 55.97959528907604,
+ },
+ {
+ lng: -3.17902317129309,
+ lat: 55.97948922305886,
+ },
+ {
+ lng: -3.179129237310268,
+ lat: 55.97938315704169,
+ },
+ {
+ lng: -3.179235303327446,
+ lat: 55.979277091024514,
+ },
+ {
+ lng: -3.179341369344624,
+ lat: 55.97917102500734,
+ },
+ {
+ lng: -3.1794474353618023,
+ lat: 55.979064958990165,
+ },
+ {
+ lng: -3.1795535013789804,
+ lat: 55.97895889297299,
+ },
+ {
+ lng: -3.1796595673961585,
+ lat: 55.978852826955816,
+ },
+ {
+ lng: -3.1797656334133366,
+ lat: 55.97874676093864,
+ },
+ {
+ lng: -3.1798716994305147,
+ lat: 55.97864069492147,
+ },
+ {
+ lng: -3.179977765447693,
+ lat: 55.97853462890429,
+ },
+ {
+ lng: -3.180083831464871,
+ lat: 55.97842856288712,
+ },
+ {
+ lng: -3.180189897482049,
+ lat: 55.97832249686994,
+ },
+ {
+ lng: -3.180295963499227,
+ lat: 55.97821643085277,
+ },
+ {
+ lng: -3.1804020295164053,
+ lat: 55.978110364835594,
+ },
+ {
+ lng: -3.1805080955335834,
+ lat: 55.97800429881842,
+ },
+ {
+ lng: -3.1806141615507615,
+ lat: 55.977898232801245,
+ },
+ {
+ lng: -3.1807202275679396,
+ lat: 55.97779216678407,
+ },
+ {
+ lng: -3.1808262935851177,
+ lat: 55.977686100766896,
+ },
+ {
+ lng: -3.180932359602296,
+ lat: 55.97758003474972,
+ },
+ {
+ lng: -3.181038425619474,
+ lat: 55.977473968732546,
+ },
+ {
+ lng: -3.181144491636652,
+ lat: 55.97736790271537,
+ },
+ {
+ lng: -3.18125055765383,
+ lat: 55.9772618366982,
+ },
+ {
+ lng: -3.1813566236710082,
+ lat: 55.97715577068102,
+ },
+ {
+ lng: -3.1814626896881864,
+ lat: 55.97704970466385,
+ },
+ {
+ lng: -3.1815687557053645,
+ lat: 55.976943638646674,
+ },
+ {
+ lng: -3.1816748217225426,
+ lat: 55.9768375726295,
+ },
+ {
+ lng: -3.1817808877397207,
+ lat: 55.976731506612325,
+ },
+ {
+ lng: -3.181886953756899,
+ lat: 55.97662544059515,
+ },
+ {
+ lng: -3.181993019774077,
+ lat: 55.976519374577975,
+ },
+ {
+ lng: -3.182099085791255,
+ lat: 55.9764133085608,
+ },
+ {
+ lng: -3.182205151808433,
+ lat: 55.976307242543626,
+ },
+ {
+ lng: -3.182311217825611,
+ lat: 55.97620117652645,
+ },
+ {
+ lng: -3.1824172838427893,
+ lat: 55.97609511050928,
+ },
+ {
+ lng: -3.1825233498599674,
+ lat: 55.9759890444921,
+ },
+ {
+ lng: -3.1826294158771455,
+ lat: 55.97588297847493,
+ },
+ {
+ lng: -3.1827354818943236,
+ lat: 55.975776912457754,
+ },
+ {
+ lng: -3.1828415479115018,
+ lat: 55.97567084644058,
+ },
+ {
+ lng: -3.18294761392868,
+ lat: 55.975564780423404,
+ },
+ {
+ lng: -3.183053679945858,
+ lat: 55.97545871440623,
+ },
+ {
+ lng: -3.183159745963036,
+ lat: 55.975352648389055,
+ },
+ {
+ lng: -3.183265811980214,
+ lat: 55.97524658237188,
+ },
+ {
+ lng: -3.1833718779973923,
+ lat: 55.975140516354706,
+ },
+ {
+ lng: -3.1834779440145704,
+ lat: 55.97503445033753,
+ },
+ {
+ lng: -3.1835840100317485,
+ lat: 55.97492838432036,
+ },
+ {
+ lng: -3.1836900760489266,
+ lat: 55.97482231830318,
+ },
+ {
+ lng: -3.1837961420661047,
+ lat: 55.97471625228601,
+ },
+ {
+ lng: -3.183902208083283,
+ lat: 55.97461018626883,
+ },
+ {
+ lng: -3.184008274100461,
+ lat: 55.97450412025166,
+ },
+ {
+ lng: -3.184114340117639,
+ lat: 55.974398054234484,
+ },
+ {
+ lng: -3.184220406134817,
+ lat: 55.97429198821731,
+ },
+ {
+ lng: -3.1843264721519953,
+ lat: 55.974185922200135,
+ },
+ {
+ lng: -3.1844325381691734,
+ lat: 55.97407985618296,
+ },
+ {
+ lng: -3.1845386041863515,
+ lat: 55.973973790165786,
+ },
+ {
+ lng: -3.1846446702035296,
+ lat: 55.97386772414861,
+ },
+ {
+ lng: -3.1847507362207077,
+ lat: 55.97376165813144,
+ },
+ {
+ lng: -3.184856802237886,
+ lat: 55.97365559211426,
+ },
+ {
+ lng: -3.1849953841677623,
+ lat: 55.97359818959941,
+ },
+ {
+ lng: -3.1851014501849404,
+ lat: 55.97349212358223,
+ },
+ {
+ lng: -3.185240032114817,
+ lat: 55.97343472106738,
+ },
+ {
+ lng: -3.185346098131995,
+ lat: 55.9733286550502,
+ },
+ {
+ lng: -3.1854846800618715,
+ lat: 55.97327125253535,
+ },
+ {
+ lng: -3.1855907460790496,
+ lat: 55.97316518651817,
+ },
+ {
+ lng: -3.185729328008926,
+ lat: 55.97310778400332,
+ },
+ {
+ lng: -3.185835394026104,
+ lat: 55.97300171798614,
+ },
+ {
+ lng: -3.1859739759559806,
+ lat: 55.97294431547129,
+ },
+ {
+ lng: -3.1860800419731587,
+ lat: 55.972838249454114,
+ },
+ {
+ lng: -3.1862186239030352,
+ lat: 55.97278084693926,
+ },
+ {
+ lng: -3.1863246899202133,
+ lat: 55.972674780922084,
+ },
+ {
+ lng: -3.18646327185009,
+ lat: 55.97261737840723,
+ },
+ {
+ lng: -3.186569337867268,
+ lat: 55.972511312390054,
+ },
+ {
+ lng: -3.1867079197971444,
+ lat: 55.9724539098752,
+ },
+ {
+ lng: -3.1868139858143225,
+ lat: 55.972347843858024,
+ },
+ {
+ lng: -3.186952567744199,
+ lat: 55.97229044134317,
+ },
+ {
+ lng: -3.187058633761377,
+ lat: 55.972184375325995,
+ },
+ {
+ lng: -3.1871972156912536,
+ lat: 55.97212697281114,
+ },
+ {
+ lng: -3.1873032817084317,
+ lat: 55.972020906793965,
+ },
+ {
+ lng: -3.187441863638308,
+ lat: 55.97196350427911,
+ },
+ {
+ lng: -3.1875479296554863,
+ lat: 55.971857438261935,
+ },
+ {
+ lng: -3.1876865115853628,
+ lat: 55.97180003574708,
+ },
+ {
+ lng: -3.187792577602541,
+ lat: 55.971693969729905,
+ },
+ {
+ lng: -3.1879311595324173,
+ lat: 55.97163656721505,
+ },
+ {
+ lng: -3.187986659939753,
+ lat: 55.97157210129231,
+ },
+ {
+ lng: -3.187986659939753,
+ lat: 55.97157210129231,
+ },
+ {
+ lng: -3.187880593922575,
+ lat: 55.971678167309484,
+ },
+ {
+ lng: -3.187774527905397,
+ lat: 55.97178423332666,
+ },
+ {
+ lng: -3.1876684618882187,
+ lat: 55.97189029934383,
+ },
+ {
+ lng: -3.1875623958710406,
+ lat: 55.97199636536101,
+ },
+ {
+ lng: -3.1874563298538625,
+ lat: 55.97210243137818,
+ },
+ {
+ lng: -3.1873502638366844,
+ lat: 55.97220849739536,
+ },
+ {
+ lng: -3.1872441978195063,
+ lat: 55.97231456341253,
+ },
+ {
+ lng: -3.187138131802328,
+ lat: 55.972420629429706,
+ },
+ {
+ lng: -3.18703206578515,
+ lat: 55.97252669544688,
+ },
+ {
+ lng: -3.186925999767972,
+ lat: 55.972632761464055,
+ },
+ {
+ lng: -3.186819933750794,
+ lat: 55.97273882748123,
+ },
+ {
+ lng: -3.1867138677336158,
+ lat: 55.972844893498404,
+ },
+ {
+ lng: -3.1866078017164376,
+ lat: 55.97295095951558,
+ },
+ {
+ lng: -3.1865017356992595,
+ lat: 55.97305702553275,
+ },
+ {
+ lng: -3.1863956696820814,
+ lat: 55.97316309154993,
+ },
+ {
+ lng: -3.1862896036649033,
+ lat: 55.9732691575671,
+ },
+ {
+ lng: -3.186183537647725,
+ lat: 55.97337522358428,
+ },
+ {
+ lng: -3.186077471630547,
+ lat: 55.97348128960145,
+ },
+ {
+ lng: -3.185971405613369,
+ lat: 55.973587355618626,
+ },
+ {
+ lng: -3.185865339596191,
+ lat: 55.9736934216358,
+ },
+ {
+ lng: -3.185759273579013,
+ lat: 55.973799487652975,
+ },
+ {
+ lng: -3.1856532075618347,
+ lat: 55.97390555367015,
+ },
+ {
+ lng: -3.1855471415446566,
+ lat: 55.974011619687325,
+ },
+ {
+ lng: -3.1854410755274785,
+ lat: 55.9741176857045,
+ },
+ {
+ lng: -3.1853350095103004,
+ lat: 55.974223751721674,
+ },
+ {
+ lng: -3.1852289434931222,
+ lat: 55.97432981773885,
+ },
+ {
+ lng: -3.185122877475944,
+ lat: 55.97443588375602,
+ },
+ {
+ lng: -3.185016811458766,
+ lat: 55.9745419497732,
+ },
+ {
+ lng: -3.184910745441588,
+ lat: 55.97464801579037,
+ },
+ {
+ lng: -3.18480467942441,
+ lat: 55.974754081807546,
+ },
+ {
+ lng: -3.1846986134072317,
+ lat: 55.97486014782472,
+ },
+ {
+ lng: -3.1845925473900536,
+ lat: 55.974966213841896,
+ },
+ {
+ lng: -3.1844864813728755,
+ lat: 55.97507227985907,
+ },
+ {
+ lng: -3.1843804153556974,
+ lat: 55.975178345876245,
+ },
+ {
+ lng: -3.1842743493385193,
+ lat: 55.97528441189342,
+ },
+ {
+ lng: -3.184168283321341,
+ lat: 55.975390477910594,
+ },
+ {
+ lng: -3.184062217304163,
+ lat: 55.97549654392777,
+ },
+ {
+ lng: -3.183956151286985,
+ lat: 55.97560260994494,
+ },
+ {
+ lng: -3.183850085269807,
+ lat: 55.97570867596212,
+ },
+ {
+ lng: -3.1837440192526287,
+ lat: 55.97581474197929,
+ },
+ {
+ lng: -3.1836379532354506,
+ lat: 55.97592080799647,
+ },
+ {
+ lng: -3.1835318872182725,
+ lat: 55.97602687401364,
+ },
+ {
+ lng: -3.1834258212010944,
+ lat: 55.976132940030816,
+ },
+ {
+ lng: -3.1833197551839163,
+ lat: 55.97623900604799,
+ },
+ {
+ lng: -3.183213689166738,
+ lat: 55.976345072065165,
+ },
+ {
+ lng: -3.18310762314956,
+ lat: 55.97645113808234,
+ },
+ {
+ lng: -3.183001557132382,
+ lat: 55.976557204099514,
+ },
+ {
+ lng: -3.182895491115204,
+ lat: 55.97666327011669,
+ },
+ {
+ lng: -3.1827894250980258,
+ lat: 55.97676933613386,
+ },
+ {
+ lng: -3.1826833590808477,
+ lat: 55.97687540215104,
+ },
+ {
+ lng: -3.1825772930636695,
+ lat: 55.97698146816821,
+ },
+ {
+ lng: -3.1824712270464914,
+ lat: 55.97708753418539,
+ },
+ {
+ lng: -3.1823651610293133,
+ lat: 55.97719360020256,
+ },
+ {
+ lng: -3.182259095012135,
+ lat: 55.977299666219736,
+ },
+ {
+ lng: -3.182153028994957,
+ lat: 55.97740573223691,
+ },
+ {
+ lng: -3.182046962977779,
+ lat: 55.977511798254085,
+ },
+ {
+ lng: -3.181940896960601,
+ lat: 55.97761786427126,
+ },
+ {
+ lng: -3.181834830943423,
+ lat: 55.977723930288434,
+ },
+ {
+ lng: -3.1817287649262447,
+ lat: 55.97782999630561,
+ },
+ {
+ lng: -3.1816226989090666,
+ lat: 55.97793606232278,
+ },
+ {
+ lng: -3.1815166328918885,
+ lat: 55.97804212833996,
+ },
+ {
+ lng: -3.1814105668747104,
+ lat: 55.97814819435713,
+ },
+ {
+ lng: -3.1813045008575322,
+ lat: 55.97825426037431,
+ },
+ {
+ lng: -3.181198434840354,
+ lat: 55.97836032639148,
+ },
+ {
+ lng: -3.181092368823176,
+ lat: 55.978466392408656,
+ },
+ {
+ lng: -3.180986302805998,
+ lat: 55.97857245842583,
+ },
+ {
+ lng: -3.18088023678882,
+ lat: 55.978678524443005,
+ },
+ {
+ lng: -3.1807741707716417,
+ lat: 55.97878459046018,
+ },
+ {
+ lng: -3.1806681047544636,
+ lat: 55.978890656477354,
+ },
+ {
+ lng: -3.1805620387372855,
+ lat: 55.97899672249453,
+ },
+ {
+ lng: -3.1804559727201074,
+ lat: 55.9791027885117,
+ },
+ {
+ lng: -3.180317390790231,
+ lat: 55.97916019102656,
+ },
+ {
+ lng: -3.180211324773053,
+ lat: 55.97926625704373,
+ },
+ {
+ lng: -3.1800727428431763,
+ lat: 55.97932365955859,
+ },
+ {
+ lng: -3.179966676825998,
+ lat: 55.97942972557576,
+ },
+ {
+ lng: -3.1798280948961217,
+ lat: 55.97948712809062,
+ },
+ {
+ lng: -3.1797220288789436,
+ lat: 55.97959319410779,
+ },
+ {
+ lng: -3.179583446949067,
+ lat: 55.97965059662265,
+ },
+ {
+ lng: -3.179477380931889,
+ lat: 55.97975666263982,
+ },
+ {
+ lng: -3.1793387990020125,
+ lat: 55.97981406515468,
+ },
+ {
+ lng: -3.1792327329848344,
+ lat: 55.97992013117185,
+ },
+ {
+ lng: -3.179094151054958,
+ lat: 55.97997753368671,
+ },
+ {
+ lng: -3.17898808503778,
+ lat: 55.98008359970388,
+ },
+ {
+ lng: -3.1788495031079034,
+ lat: 55.98014100221874,
+ },
+ {
+ lng: -3.1787434370907253,
+ lat: 55.98024706823591,
+ },
+ {
+ lng: -3.178604855160849,
+ lat: 55.98030447075077,
+ },
+ {
+ lng: -3.1784987891436707,
+ lat: 55.98041053676794,
+ },
+ {
+ lng: -3.178360207213794,
+ lat: 55.980467939282796,
+ },
+ {
+ lng: -3.178254141196616,
+ lat: 55.98057400529997,
+ },
+ {
+ lng: -3.1781155592667396,
+ lat: 55.980631407814826,
+ },
+ {
+ lng: -3.1780094932495615,
+ lat: 55.980737473832,
+ },
+ {
+ lng: -3.177870911319685,
+ lat: 55.980794876346856,
+ },
+ {
+ lng: -3.177764845302507,
+ lat: 55.98090094236403,
+ },
+ {
+ lng: -3.1776262633726304,
+ lat: 55.980958344878886,
+ },
+ {
+ lng: -3.1775201973554523,
+ lat: 55.98106441089606,
+ },
+ {
+ lng: -3.177381615425576,
+ lat: 55.981121813410915,
+ },
+ {
+ lng: -3.17732611501824,
+ lat: 55.981186279333656,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+};
diff --git a/drone-frontend/src/stores.js b/drone-frontend/src/stores.js
new file mode 100644
index 0000000..290fe0d
--- /dev/null
+++ b/drone-frontend/src/stores.js
@@ -0,0 +1,3 @@
+import { writable } from 'svelte/store';
+
+export const theme = writable('dark');
diff --git a/drone-frontend/src/utils.js b/drone-frontend/src/utils.js
new file mode 100644
index 0000000..b41e412
--- /dev/null
+++ b/drone-frontend/src/utils.js
@@ -0,0 +1,108 @@
+import { fallbackBounds } from "./sampleData.js";
+
+export function buildTimeline(response) {
+ const drones = [];
+ const allPoints = [];
+ let longest = 0;
+
+ if (!response || !response.dronePaths) {
+ return { drones, totalSteps: 0, bounds: fallbackBounds };
+ }
+
+ for (const path of response.dronePaths) {
+ const coords = flattenDeliveries(path.deliveries);
+ if (!coords.length) continue;
+ drones.push({ id: path.droneId, path: coords });
+ coords.forEach((p) => allPoints.push(p));
+ longest = Math.max(longest, coords.length);
+ }
+
+ // Add padding points for bounds calculation if empty
+ const basePoints =
+ allPoints.length > 0
+ ? allPoints
+ : [
+ {
+ lng: fallbackBounds.minLng,
+ lat: fallbackBounds.minLat,
+ },
+ {
+ lng: fallbackBounds.maxLng,
+ lat: fallbackBounds.maxLat,
+ },
+ ];
+
+ return {
+ drones,
+ totalSteps: longest || 0,
+ bounds: computeBounds(basePoints),
+ };
+}
+
+function flattenDeliveries(deliveries) {
+ const coords = [];
+ for (const delivery of deliveries || []) {
+ for (const point of delivery.flightPath || []) {
+ if (
+ !coords.length ||
+ !samePoint(coords[coords.length - 1], point)
+ ) {
+ coords.push(point);
+ }
+ }
+ }
+ return coords;
+}
+
+function samePoint(a, b) {
+ return Math.abs(a.lng - b.lng) < 1e-9 && Math.abs(a.lat - b.lat) < 1e-9;
+}
+
+export function computeBounds(points) {
+ if (!points.length) return fallbackBounds;
+ let minLng = Infinity,
+ maxLng = -Infinity,
+ minLat = Infinity,
+ maxLat = -Infinity;
+ for (const p of points) {
+ minLng = Math.min(minLng, p.lng);
+ maxLng = Math.max(maxLng, p.lng);
+ minLat = Math.min(minLat, p.lat);
+ maxLat = Math.max(maxLat, p.lat);
+ }
+ const padding = 0.002; // Slightly more padding
+ return {
+ minLng: minLng - padding,
+ maxLng: maxLng + padding,
+ minLat: minLat - padding,
+ maxLat: maxLat + padding,
+ };
+}
+
+export function colorFor(idx, palette) {
+ if (!palette || palette.length === 0) return "#000";
+ return palette[idx % palette.length];
+}
+
+export function formatDuration(sec) {
+ const m = Math.floor(sec / 60)
+ .toString()
+ .padStart(2, "0");
+ const s = Math.floor(sec % 60)
+ .toString()
+ .padStart(2, "0");
+ return `${m}:${s}`;
+}
+
+export function computeWallClock(start, seconds) {
+ const base = start ? new Date(start) : new Date();
+ const ts = new Date(base.getTime() + seconds * 1000);
+ return isNaN(ts.getTime())
+ ? new Date().toISOString()
+ : ts.toISOString();
+}
+
+export function boundsKey(bounds) {
+ if (!bounds) return "";
+ return `${bounds.minLat}:${bounds.maxLat}:${bounds.minLng}:${bounds.maxLng}`;
+}
diff --git a/drone-frontend/svelte.config.js b/drone-frontend/svelte.config.js
new file mode 100644
index 0000000..21b9399
--- /dev/null
+++ b/drone-frontend/svelte.config.js
@@ -0,0 +1,5 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+export default {
+ preprocess: vitePreprocess()
+};
diff --git a/drone-frontend/tailwind.config.js b/drone-frontend/tailwind.config.js
new file mode 100644
index 0000000..92c4c79
--- /dev/null
+++ b/drone-frontend/tailwind.config.js
@@ -0,0 +1,27 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ["./index.html", "./src/**/*.{svelte,js,ts,jsx,tsx}"],
+ darkMode: "class",
+ theme: {
+ extend: {
+ colors: {
+ // Palette for drone paths
+ // Light theme
+ "p-light-1": "#16a34a", // green-600
+ "p-light-2": "#ca8a04", // yellow-600
+ "p-light-3": "#0ea5e9", // sky-500
+ "p-light-4": "#db2777", // pink-600
+ "p-light-5": "#7c3aed", // violet-600
+ "p-light-6": "#dc2626", // red-600
+
+ "p-dark-1": "#00ff00", // Lime
+ "p-dark-2": "#ffff00", // Yellow
+ "p-dark-3": "#00ffff", // Cyan
+ "p-dark-4": "#ff00ff", // Magenta
+ "p-dark-5": "#bd00ff", // Violet
+ "p-dark-6": "#ff4d4d", // Red-ish
+ },
+ },
+ },
+ plugins: [],
+};
diff --git a/drone-frontend/vite.config.js b/drone-frontend/vite.config.js
new file mode 100644
index 0000000..6a8ead9
--- /dev/null
+++ b/drone-frontend/vite.config.js
@@ -0,0 +1,9 @@
+import { svelte } from '@sveltejs/vite-plugin-svelte';
+
+export default {
+ plugins: [svelte()],
+ server: {
+ port: 4173,
+ strictPort: true
+ }
+};
diff --git a/flake.nix b/flake.nix
index 834f374..cfb17de 100644
--- a/flake.nix
+++ b/flake.nix
@@ -35,6 +35,8 @@
oha
gopls
go
+ bun
+ svelte-language-server
];
shellHook = ''
export JAVA_HOME=${pkgs.jdk21}
diff --git a/ilp-rest-service/ilp-cw-api/Front-end Test/1 Drone.bru b/ilp-rest-service/ilp-cw-api/Front-end Test/1 Drone.bru
new file mode 100644
index 0000000..0817018
--- /dev/null
+++ b/ilp-rest-service/ilp-cw-api/Front-end Test/1 Drone.bru
@@ -0,0 +1,39 @@
+meta {
+ name: 1 Drone
+ type: http
+ seq: 5
+}
+
+post {
+ url: {{API_BASE}}/calcDeliveryPath
+ body: json
+ auth: inherit
+}
+
+body:json {
+ [
+ {
+ "id": 123,
+ "date": "2025-12-22",
+ "time": "14:30",
+ "requirements": {
+ "capacity": 0.75,
+ "heating": true,
+ "maxCost": 13.5
+ },
+ "delivery": {
+ "lng": -3.191499773625054,
+ "lat": 55.9510924649214
+ }
+ }
+ ]
+}
+
+assert {
+ res.status: eq 200
+}
+
+settings {
+ encodeUrl: true
+ timeout: 0
+}
diff --git a/ilp-rest-service/ilp-cw-api/Front-end Test/2 Drones.bru b/ilp-rest-service/ilp-cw-api/Front-end Test/2 Drones.bru
new file mode 100644
index 0000000..072c15f
--- /dev/null
+++ b/ilp-rest-service/ilp-cw-api/Front-end Test/2 Drones.bru
@@ -0,0 +1,53 @@
+meta {
+ name: 2 Drones
+ type: http
+ seq: 9
+}
+
+post {
+ url: {{API_BASE}}/calcDeliveryPath
+ body: json
+ auth: inherit
+}
+
+body:json {
+ [
+ {
+ "id": 123,
+ "date": "2025-12-22",
+ "time": "14:30",
+ "requirements": {
+ "capacity": 0.75,
+ "heating": true,
+ "maxCost": 13.5
+ },
+ "delivery": {
+ "lng": -3.191499773625054,
+ "lat": 55.9510924649214
+ }
+ },
+ {
+ "id": 456,
+ "date": "2025-12-25",
+ "time": "11:30",
+ "requirements": {
+ "capacity": 0.75,
+ "heating": true,
+ "maxCost": 13.5
+ },
+ "delivery": {
+ "lng": -3.187986659939753,
+ "lat": 55.97157210129231
+ }
+ }
+ ]
+}
+
+assert {
+ res.status: eq 200
+}
+
+settings {
+ encodeUrl: true
+ timeout: 0
+}
diff --git a/ilp-rest-service/ilp-cw-api/Front-end Test/folder.bru b/ilp-rest-service/ilp-cw-api/Front-end Test/folder.bru
new file mode 100644
index 0000000..8fb7e32
--- /dev/null
+++ b/ilp-rest-service/ilp-cw-api/Front-end Test/folder.bru
@@ -0,0 +1,8 @@
+meta {
+ name: Front-end Test
+ seq: 8
+}
+
+auth {
+ mode: inherit
+}
diff --git a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass Bayes Centre.bru b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass Bayes Centre.bru
index 7084eb7..aa7562b 100644
--- a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass Bayes Centre.bru
+++ b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass Bayes Centre.bru
@@ -1,7 +1,7 @@
meta {
name: Bypass Bayes Centre
type: http
- seq: 10
+ seq: 7
}
post {
diff --git a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass RestrictedArea.bru b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass RestrictedArea.bru
index ef4b750..069722c 100644
--- a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass RestrictedArea.bru
+++ b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Bypass RestrictedArea.bru
@@ -1,7 +1,7 @@
meta {
name: Bypass RestrictedArea
type: http
- seq: 9
+ seq: 6
}
post {
diff --git a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/From Appleton Tower.bru b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/From Appleton Tower.bru
index 32154f1..901f6dc 100644
--- a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/From Appleton Tower.bru
+++ b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/From Appleton Tower.bru
@@ -1,7 +1,7 @@
meta {
name: From Appleton Tower
type: http
- seq: 6
+ seq: 3
}
post {
diff --git a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple- Path.bru b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple- Path.bru
index 976fb1f..84b46a0 100644
--- a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple- Path.bru
+++ b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple- Path.bru
@@ -1,7 +1,7 @@
meta {
name: Simple: Path
type: http
- seq: 4
+ seq: 2
}
post {
diff --git a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple.bru b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple.bru
index 27ebae3..2aed73e 100644
--- a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple.bru
+++ b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Simple.bru
@@ -1,7 +1,7 @@
meta {
name: Simple
type: http
- seq: 4
+ seq: 1
}
post {
diff --git a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Two Points from two service points.bru b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Two Points from two service points.bru
index fe0167e..698ad45 100644
--- a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Two Points from two service points.bru
+++ b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/Two Points from two service points.bru
@@ -1,7 +1,7 @@
meta {
name: Two Points from two service points
type: http
- seq: 8
+ seq: 5
}
post {
diff --git a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/[400] DeliveryPoint in RestrictedArea.bru b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/[400] DeliveryPoint in RestrictedArea.bru
index 0bde63d..1cbc622 100644
--- a/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/[400] DeliveryPoint in RestrictedArea.bru
+++ b/ilp-rest-service/ilp-cw-api/[POST] calcDeliveryPath(AsGeoJson)/[400] DeliveryPoint in RestrictedArea.bru
@@ -1,7 +1,7 @@
meta {
name: [400] DeliveryPoint in RestrictedArea
type: http
- seq: 7
+ seq: 4
}
post {