diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 58a4d51..7d860d5 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -14,5 +14,9 @@ jobs: distribution: temurin java-version: 25 cache: gradle + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest - name: Build with Gradle - run: chmod a+x gradlew && ./gradlew build --no-daemon \ No newline at end of file + run: chmod a+x gradlew && ./gradlew build --no-daemon diff --git a/.gitignore b/.gitignore index 8ca703b..130eae9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,7 @@ *.iml /target/ /src/main/resources/build.properties -/src/main/resources/httpd/assets/textures/ -/src/main/resources/httpd/assets/models/ -/src/main/resources/httpd/assets/items/ -/src/main/resources/httpd/assets/.minecraft-version +/src/main/frontend/node_modules/ # OS .DS_Store @@ -21,4 +18,7 @@ Thumbs.db /.gradle/ # Decompiled Minecraft sources (downloaded by scripts/download-minecraft-source.ps1) -/minecraft-source/ \ No newline at end of file +/minecraft-source/ + +# Local mirror of the runtime HTTPD Minecraft asset cache +/minecraft-assets/ diff --git a/build.gradle.kts b/build.gradle.kts index 013d047..357383e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,6 +30,12 @@ repositories { } } +sourceSets { + main { + resources.srcDir(layout.buildDirectory.dir("generated/frontend-resources")) + } +} + dependencies { implementation("org.projectlombok:lombok:1.18.46") annotationProcessor("org.projectlombok:lombok:1.18.46") @@ -46,17 +52,41 @@ dependencies { implementation("commons-io:commons-io:2.22.0") } +val frontendDir = layout.projectDirectory.dir("src/main/frontend") +val frontendOutputDir = layout.buildDirectory.dir("generated/frontend-resources/httpd/app") + +tasks.register("bunInstallFrontend") { + workingDir = frontendDir.asFile + commandLine("bun", "install", "--frozen-lockfile") + inputs.files( + frontendDir.file("package.json"), + frontendDir.file("bun.lock") + ) + outputs.dir(frontendDir.dir("node_modules")) +} + +tasks.register("buildFrontend") { + workingDir = frontendDir.asFile + commandLine("bun", "run", "build") + dependsOn("bunInstallFrontend") + inputs.dir(frontendDir.dir("src")) + inputs.files( + frontendDir.file("index.html"), + frontendDir.file("vite.config.ts"), + frontendDir.file("svelte.config.js"), + frontendDir.file("tsconfig.json"), + frontendDir.file("tsconfig.node.json"), + frontendDir.file("components.json"), + frontendDir.file("package.json"), + frontendDir.file("bun.lock") + ) + outputs.dir(frontendOutputDir) +} + tasks.getByName("jar") { duplicatesStrategy = DuplicatesStrategy.EXCLUDE archiveBaseName.set("Module-HTTPD") archiveVersion.set("") - from("src/main/resources") { - exclude("dev/**") - exclude("httpd/assets/textures/**") - exclude("httpd/assets/models/**") - exclude("httpd/assets/items/**") - exclude("httpd/assets/.minecraft-version") - } } java { @@ -71,11 +101,10 @@ tasks { options.encoding = Charsets.UTF_8.name() } processResources { + dependsOn("buildFrontend") filteringCharset = Charsets.UTF_8.name() - exclude("httpd/assets/textures/**") - exclude("httpd/assets/models/**") - exclude("httpd/assets/items/**") - exclude("httpd/assets/.minecraft-version") + exclude("dev/**") + exclude("httpd/assets/**") } } diff --git a/scripts/download-minecraft-assets.ps1 b/scripts/download-minecraft-assets.ps1 index 8b4fe46..0425ecf 100644 --- a/scripts/download-minecraft-assets.ps1 +++ b/scripts/download-minecraft-assets.ps1 @@ -1,18 +1,22 @@ param( - [string]$Version = "" + [string]$Version = "", + [string]$AssetRoot = "" ) # Downloads the vanilla Minecraft assets used by the HTTPD live inventory view -# into src/main/resources/httpd/assets for local development. +# into the same minecraft-assets cache layout used by the running module. # # Usage: # ./scripts/download-minecraft-assets.ps1 # latest release -# ./scripts/download-minecraft-assets.ps1 1.21.10 # specific version +# ./scripts/download-minecraft-assets.ps1 26.1.2 # specific version +# ./scripts/download-minecraft-assets.ps1 -AssetRoot "plugins/Plex/modules//minecraft-assets" $ErrorActionPreference = "Stop" $ProjectRoot = Resolve-Path (Join-Path $PSScriptRoot "..") -$AssetRoot = Join-Path $ProjectRoot "src/main/resources/httpd/assets" +if ([string]::IsNullOrWhiteSpace($AssetRoot)) { + $AssetRoot = if ($env:PLEX_HTTPD_ASSET_ROOT) { $env:PLEX_HTTPD_ASSET_ROOT } else { Join-Path $ProjectRoot "minecraft-assets" } +} $ManifestUrl = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" $manifest = Invoke-RestMethod -Uri $ManifestUrl -TimeoutSec 30 @@ -76,7 +80,7 @@ try { $zip.Dispose() } - Set-Content -Path (Join-Path $AssetRoot ".minecraft-version") -Value $Version -Encoding UTF8 + Set-Content -Path (Join-Path $AssetRoot "version.txt") -Value $Version -Encoding UTF8 Write-Host "Extracted $extracted files to $AssetRoot" } finally { diff --git a/scripts/download-minecraft-assets.sh b/scripts/download-minecraft-assets.sh index cb588ab..baa5681 100644 --- a/scripts/download-minecraft-assets.sh +++ b/scripts/download-minecraft-assets.sh @@ -2,15 +2,16 @@ set -eu # Downloads the vanilla Minecraft assets used by the HTTPD live inventory view -# into src/main/resources/httpd/assets for local development. +# into the same minecraft-assets cache layout used by the running module. # # Usage: # ./scripts/download-minecraft-assets.sh # latest release -# ./scripts/download-minecraft-assets.sh 1.21.10 # specific version +# ./scripts/download-minecraft-assets.sh 26.1.2 # specific version +# ./scripts/download-minecraft-assets.sh 26.1.2 "plugins/Plex/modules//minecraft-assets" VERSION="${1:-}" PROJECT_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)" -ASSET_ROOT="$PROJECT_ROOT/src/main/resources/httpd/assets" +ASSET_ROOT="${2:-${PLEX_HTTPD_ASSET_ROOT:-$PROJECT_ROOT/minecraft-assets}}" python3 - "$VERSION" "$ASSET_ROOT" <<'PY' import json @@ -71,6 +72,6 @@ with tempfile.TemporaryDirectory() as tmp: shutil.copyfileobj(source, out) extracted += 1 -(asset_root / ".minecraft-version").write_text(version + "\n", encoding="utf-8") +(asset_root / "version.txt").write_text(version + "\n", encoding="utf-8") print(f"Extracted {extracted} files to {asset_root}") PY diff --git a/scripts/download-minecraft-source.ps1 b/scripts/download-minecraft-source.ps1 index a44a8dd..8ac5a4c 100644 --- a/scripts/download-minecraft-source.ps1 +++ b/scripts/download-minecraft-source.ps1 @@ -29,10 +29,10 @@ function Download-IfMissing($Url, $Target) { Invoke-WebRequest -Uri $Url -OutFile $Target -TimeoutSec 600 } -# Resolve version (explicit > cached > latest) +# Resolve version (explicit > local debug asset cache > latest) $manifest = Invoke-RestMethod -Uri $ManifestUrl -TimeoutSec 30 if ([string]::IsNullOrWhiteSpace($Version)) { - $cachedFile = Join-Path $ProjectRoot "src/main/resources/httpd/assets/.minecraft-version" + $cachedFile = Join-Path $ProjectRoot "minecraft-assets/version.txt" if (Test-Path $cachedFile) { $Version = (Get-Content $cachedFile -Raw).Trim() } else { diff --git a/src/main/frontend/bun.lock b/src/main/frontend/bun.lock new file mode 100644 index 0000000..39577e5 --- /dev/null +++ b/src/main/frontend/bun.lock @@ -0,0 +1,302 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "plex-httpd-frontend", + "dependencies": { + "@hugeicons/core-free-icons": "^4.1.3", + "@hugeicons/svelte": "^1.1.2", + "clsx": "^2.1.1", + "tailwind-merge": "^3.6.0", + "three": "^0.184.0", + "tw-animate-css": "^1.4.0", + }, + "devDependencies": { + "@fontsource-variable/figtree": "^5.2.10", + "@internationalized/date": "^3.12.1", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "@tailwindcss/vite": "^4.3.0", + "@types/node": "^25.7.0", + "@types/three": "^0.184.1", + "bits-ui": "^2.18.1", + "shadcn-svelte": "^1.2.7", + "svelte": "^5.55.5", + "svelte-check": "^4.4.8", + "tailwind-variants": "^3.2.2", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.12", + }, + }, + }, + "packages": { + "@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="], + + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@fontsource-variable/figtree": ["@fontsource-variable/figtree@5.2.10", "", {}, "sha512-a5Gumbpy3mdd+Yg31g6Qb7CmjYbrfyutJa3bWfP5q8A4GclIOwX7mI+ZuSHsJnw/mHvW6r9oh1AHJcJTIxK4JA=="], + + "@hugeicons/core-free-icons": ["@hugeicons/core-free-icons@4.1.3", "", {}, "sha512-FWPrKnlYKpSaitUtlZhFlDQXDgHiayTPFJYWvyIKkW2RI6Vj5KBvjxI+lAnnFPu07SwgIMiDDj+Gttl0t+o/oQ=="], + + "@hugeicons/svelte": ["@hugeicons/svelte@1.1.2", "", { "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-yYmLFE+tq/mJmmjNaGxnaA99+H9yOzZmWJOMxKd9g9EJ8FCTCG8jpTgzVSv5bpuXDjQZFIGJz+Z/syiXhrS0mA=="], + + "@internationalized/date": ["@internationalized/date@3.12.1", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ=="], + + "@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=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@oxc-project/types": ["@oxc-project/types@0.129.0", "", {}, "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0", "", { "os": "linux", "cpu": "arm" }, "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0", "", { "os": "none", "cpu": "arm64" }, "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig=="], + + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0", "", {}, "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ=="], + + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], + + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.1.2", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA=="], + + "@swc/helpers": ["@swc/helpers@0.5.21", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.3.0", "", { "dependencies": { "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw=="], + + "@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/node": ["@types/node@25.7.0", "", { "dependencies": { "undici-types": "~7.21.0" } }, "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg=="], + + "@types/stats.js": ["@types/stats.js@0.17.4", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="], + + "@types/three": ["@types/three@0.184.1", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": ">=0.5.17", "fflate": "~0.8.2", "meshoptimizer": "~1.1.1" } }, "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA=="], + + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + + "@types/webxr": ["@types/webxr@0.5.24", "", {}, "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg=="], + + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "bits-ui": ["bits-ui@2.18.1", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.35.1", "svelte-toolbelt": "^0.10.6", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-KkemzKFH4T3gt3H+P86JcnAWExjByv/6vlwjm/BoCwTPHu03yiCdxbghdJLvFReQTe0acCAiRcKfmixxD6XvlA=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "devalue": ["devalue@5.8.1", "", {}, "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw=="], + + "enhanced-resolve": ["enhanced-resolve@5.21.5", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A=="], + + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + + "esrap": ["esrap@2.2.9", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-4KijP+NxCWthMCUC3qHbE6n4vCjqgJS1uAYKhuT/GWfFTf1Qyive2TgOjep+gzbSzRfnNyaN/UU9YmdOt8Eg0A=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fflate": ["fflate@0.8.3", "", {}, "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="], + + "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=="], + + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "meshoptimizer": ["meshoptimizer@1.1.1", "", {}, "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + + "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], + + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "rolldown": ["rolldown@1.0.0", "", { "dependencies": { "@oxc-project/types": "=0.129.0", "@rolldown/pluginutils": "1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0", "@rolldown/binding-darwin-arm64": "1.0.0", "@rolldown/binding-darwin-x64": "1.0.0", "@rolldown/binding-freebsd-x64": "1.0.0", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", "@rolldown/binding-linux-arm64-gnu": "1.0.0", "@rolldown/binding-linux-arm64-musl": "1.0.0", "@rolldown/binding-linux-ppc64-gnu": "1.0.0", "@rolldown/binding-linux-s390x-gnu": "1.0.0", "@rolldown/binding-linux-x64-gnu": "1.0.0", "@rolldown/binding-linux-x64-musl": "1.0.0", "@rolldown/binding-openharmony-arm64": "1.0.0", "@rolldown/binding-wasm32-wasi": "1.0.0", "@rolldown/binding-win32-arm64-msvc": "1.0.0", "@rolldown/binding-win32-x64-msvc": "1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA=="], + + "runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0" }, "optionalPeers": ["@sveltejs/kit"] }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="], + + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + + "shadcn-svelte": ["shadcn-svelte@1.2.7", "", { "dependencies": { "commander": "^14.0.0", "node-fetch-native": "^1.6.4", "postcss": "^8.5.5", "tailwind-merge": "^3.0.0" }, "peerDependencies": { "svelte": "^5.0.0" }, "bin": { "shadcn-svelte": "dist/index.mjs" } }, "sha512-mWuQk4H4gtV+J2wJQ7nEPKNnB/v86AALFryZU0SSM7ChHmJJMZ1kH+qIuxYKrXm9vOOOcSWHRsWzPDB71DnjYA=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + + "svelte": ["svelte@5.55.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw=="], + + "svelte-check": ["svelte-check@4.4.8", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w=="], + + "svelte-toolbelt": ["svelte-toolbelt@0.10.6", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.35.1", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ=="], + + "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="], + + "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], + + "tailwind-variants": ["tailwind-variants@3.2.2", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg=="], + + "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + + "three": ["three@0.184.0", "", {}, "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg=="], + + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], + + "undici-types": ["undici-types@7.21.0", "", {}, "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ=="], + + "vite": ["vite@8.0.12", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", "rolldown": "1.0.0", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg=="], + + "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], + + "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + } +} diff --git a/src/main/frontend/bunfig.toml b/src/main/frontend/bunfig.toml new file mode 100644 index 0000000..8ee15ae --- /dev/null +++ b/src/main/frontend/bunfig.toml @@ -0,0 +1,2 @@ +[install] +minimumReleaseAge = 604800 \ No newline at end of file diff --git a/src/main/frontend/components.json b/src/main/frontend/components.json new file mode 100644 index 0000000..51948b6 --- /dev/null +++ b/src/main/frontend/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/app.css", + "baseColor": "neutral" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils", + "ui": "$lib/components/ui", + "hooks": "$lib/hooks", + "lib": "$lib" + }, + "typescript": true, + "registry": "https://shadcn-svelte.com/registry", + "style": "maia", + "iconLibrary": "hugeicons", + "menuColor": "default", + "menuAccent": "subtle" +} diff --git a/src/main/frontend/index.html b/src/main/frontend/index.html new file mode 100644 index 0000000..afd5043 --- /dev/null +++ b/src/main/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + Plex HTTPD + + +
+ + + diff --git a/src/main/frontend/package.json b/src/main/frontend/package.json new file mode 100644 index 0000000..5cdea2d --- /dev/null +++ b/src/main/frontend/package.json @@ -0,0 +1,34 @@ +{ + "name": "plex-httpd-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "vite build", + "check": "svelte-check --tsconfig ./tsconfig.json" + }, + "dependencies": { + "@hugeicons/core-free-icons": "^4.1.3", + "@hugeicons/svelte": "^1.1.2", + "clsx": "^2.1.1", + "tailwind-merge": "^3.6.0", + "three": "^0.184.0", + "tw-animate-css": "^1.4.0" + }, + "devDependencies": { + "@fontsource-variable/figtree": "^5.2.10", + "@internationalized/date": "^3.12.1", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "@tailwindcss/vite": "^4.3.0", + "@types/node": "^25.7.0", + "@types/three": "^0.184.1", + "bits-ui": "^2.18.1", + "shadcn-svelte": "^1.2.7", + "svelte": "^5.55.5", + "svelte-check": "^4.4.8", + "tailwind-variants": "^3.2.2", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.12" + } +} diff --git a/src/main/frontend/src/App.svelte b/src/main/frontend/src/App.svelte new file mode 100644 index 0000000..6098169 --- /dev/null +++ b/src/main/frontend/src/App.svelte @@ -0,0 +1,106 @@ + + + + {#if route.path === 'home'} + {#await import('$lib/pages/HomePage.svelte') then {default: HomePage}} + + {/await} + {:else if route.path === 'players'} + {#if auth === null} +

Loading players...

+ {:else} + {#await import('$lib/pages/PlayersPage.svelte') then {default: PlayersPage}} + + {/await} + {/if} + {:else if route.path === 'player'} + {#if staff} + {#await import('$lib/pages/PlayerPage.svelte') then {default: PlayerPage}} + + {/await} + {:else} + + {/if} + {:else if route.path === 'commands'} + {#await import('$lib/pages/CommandsPage.svelte') then {default: CommandsPage}} + + {/await} + {:else if route.path === 'punishments'} + {#await import('$lib/pages/PunishmentsSearchPage.svelte') then {default: PunishmentsSearchPage}} + + {/await} + {:else if route.path === 'punishments-detail'} + {#await import('$lib/pages/PunishmentsDetailPage.svelte') then {default: PunishmentsDetailPage}} + + {/await} + {:else if route.path === 'indefbans'} + {#if staff} + {#await import('$lib/pages/IndefBansPage.svelte') then {default: IndefBansPage}} + + {/await} + {:else} + + {/if} + {:else if route.path === 'schematics'} + {#await import('$lib/pages/SchematicsPage.svelte') then {default: SchematicsPage}} + + {/await} + {:else if route.path === 'schematics-upload'} + {#if staff} + {#await import('$lib/pages/SchematicUploadPage.svelte') then {default: SchematicUploadPage}} + + {/await} + {:else} + + {/if} + {:else} +
+

Not found

+

No frontend route matches this path.

+
+ {/if} +
diff --git a/src/main/frontend/src/app.css b/src/main/frontend/src/app.css new file mode 100644 index 0000000..4613439 --- /dev/null +++ b/src/main/frontend/src/app.css @@ -0,0 +1,243 @@ +@import "tailwindcss"; +@import "tw-animate-css"; +@import "shadcn-svelte/tailwind.css"; +@import "@fontsource-variable/figtree"; + +@custom-variant dark (&:is(.dark *)); + +@theme { + --font-sans: 'Figtree Variable', sans-serif; + --font-mono: "Geist Mono", ui-monospace, "JetBrains Mono", monospace; + + --radius: 0.625rem; + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --radius-2xl: calc(var(--radius) + 8px); + + --color-background: oklch(1 0 0); + --color-foreground: oklch(0.145 0 0); + --color-card: oklch(1 0 0); + --color-card-foreground: oklch(0.145 0 0); + --color-popover: oklch(1 0 0); + --color-popover-foreground: oklch(0.145 0 0); + --color-primary: oklch(0.555 0.265 264); + --color-primary-foreground: oklch(0.985 0 0); + --color-secondary: oklch(0.97 0 0); + --color-secondary-foreground: oklch(0.205 0 0); + --color-muted: oklch(0.97 0 0); + --color-muted-foreground: oklch(0.556 0 0); + --color-accent: oklch(0.97 0 0); + --color-accent-foreground: oklch(0.205 0 0); + --color-destructive: oklch(0.58 0.22 27); + --color-destructive-foreground: oklch(0.985 0 0); + --color-success: oklch(0.62 0.18 145); + --color-success-foreground: oklch(0.985 0 0); + --color-warning: oklch(0.74 0.16 75); + --color-warning-foreground: oklch(0.145 0 0); + --color-border: oklch(0.922 0 0); + --color-input: oklch(0.922 0 0); + --color-ring: oklch(0.555 0.265 264); + --color-surface: oklch(0.98 0 0); + --color-surface-foreground: oklch(0.145 0 0); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --radius-3xl: calc(var(--radius) * 2.2); + --radius-4xl: calc(var(--radius) * 2.6); +} + +:root { + color-scheme: light; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + color-scheme: dark; + --color-background: oklch(0.145 0 0); + --color-foreground: oklch(0.985 0 0); + --color-card: oklch(0.205 0 0); + --color-card-foreground: oklch(0.985 0 0); + --color-popover: oklch(0.205 0 0); + --color-popover-foreground: oklch(0.985 0 0); + --color-primary: oklch(0.62 0.235 264); + --color-primary-foreground: oklch(0.985 0 0); + --color-secondary: oklch(0.269 0 0); + --color-secondary-foreground: oklch(0.985 0 0); + --color-muted: oklch(0.269 0 0); + --color-muted-foreground: oklch(0.708 0 0); + --color-accent: oklch(0.371 0 0); + --color-accent-foreground: oklch(0.985 0 0); + --color-destructive: oklch(0.704 0.191 22); + --color-destructive-foreground: oklch(0.985 0 0); + --color-success: oklch(0.74 0.18 145); + --color-success-foreground: oklch(0.145 0 0); + --color-warning: oklch(0.82 0.16 75); + --color-warning-foreground: oklch(0.145 0 0); + --color-border: oklch(1 0 0 / 10%); + --color-input: oklch(1 0 0 / 15%); + --color-ring: oklch(0.62 0.235 264); + --color-surface: oklch(0.2 0 0); + --color-surface-foreground: oklch(0.708 0 0); + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + border-color: var(--color-border); + @apply border-border outline-ring/50; + } + + html { + scrollbar-gutter: stable; + @apply font-sans; + } + + body { + min-height: 100vh; + margin: 0; + overflow-x: hidden; + background: var(--color-background); + color: var(--color-foreground); + font-family: var(--font-sans); + font-feature-settings: "cv11", "ss01"; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + @apply bg-background text-foreground; + } + + body::before { + content: ""; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background-image: radial-gradient(circle at 1px 1px, oklch(from var(--color-foreground) l c h / 0.05) 1px, transparent 0); + background-size: 28px 28px; + mask-image: linear-gradient(to bottom, black 0%, transparent 100%); + } + + body::after { + content: ""; + position: fixed; + inset-inline: 0; + bottom: 0; + z-index: 0; + height: 45vh; + pointer-events: none; + background: linear-gradient(to top, oklch(from var(--color-primary) calc(l - 0.05) c h / 0.12), transparent); + } + + button, + input, + select, + textarea { + font: inherit; + } +} + +@layer utilities { + .layer-content { + position: relative; + z-index: 1; + } + + .ring-card { + box-shadow: inset 0 0 0 1px oklch(from var(--color-foreground) l c h / 0.08); + } + + .tabular { + font-variant-numeric: tabular-nums; + } + + .inventory-pixelated { + image-rendering: pixelated; + } +} + +@keyframes rise { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.rise { + animation: rise 0.35s cubic-bezier(0.16, 0.84, 0.32, 1) backwards; +} diff --git a/src/main/frontend/src/lib/api.ts b/src/main/frontend/src/lib/api.ts new file mode 100644 index 0000000..1895f88 --- /dev/null +++ b/src/main/frontend/src/lib/api.ts @@ -0,0 +1,66 @@ +import type { + AuthState, + CommandGroup, + PlayerDetails, + PunishmentsPayload, + Schematic +} from '$lib/types/api'; + +export async function getJson(url: string, timeoutMs = 15_000): Promise { + const controller = new AbortController(); + const timeout = window.setTimeout(() => controller.abort(), timeoutMs); + try { + const response = await fetch(url, { + credentials: 'same-origin', + headers: {Accept: 'application/json'}, + signal: controller.signal + }); + const body = await response.json().catch(() => null); + if (!response.ok || (body && typeof body === 'object' && 'error' in body)) { + const message = body && typeof body === 'object' && 'error' in body ? String(body.error) : `${response.status} ${response.statusText}`; + throw new Error(message); + } + return body as T; + } catch (cause) { + if (cause instanceof DOMException && cause.name === 'AbortError') { + throw new Error('Request timed out.'); + } + throw cause; + } finally { + window.clearTimeout(timeout); + } +} + +export async function getAuth(): Promise { + const response = await fetch('/oauth2/me', { + credentials: 'same-origin', + headers: {Accept: 'application/json'} + }); + const body = await response.json().catch(() => null); + if (body && typeof body === 'object' && 'authenticated' in body) return body as AuthState; + return {authenticated: false}; +} + +export function postForm(url: string, form: FormData): Promise { + return fetch(url, { + method: 'POST', + credentials: 'same-origin', + body: form, + headers: {Accept: 'application/json'} + }).then(async (response) => { + const body = await response.json().catch(() => null); + if (!response.ok || (body && typeof body === 'object' && body.ok === false)) { + const message = body?.error ?? body?.message ?? `${response.status} ${response.statusText}`; + throw new Error(String(message)); + } + return body as T; + }); +} + +export const api = { + commands: () => getJson<{ groups: CommandGroup[] }>('/api/commands/'), + player: (id: string) => getJson<{ player: PlayerDetails }>(`/api/player/${encodeURIComponent(id)}`), + punishments: (id: string) => getJson(`/api/punishments/${encodeURIComponent(id)}`), + indefiniteBans: () => getJson>>('/api/indefbans/'), + schematics: () => getJson<{ schematics: Schematic[] }>('/api/schematics/list') +}; diff --git a/src/main/resources/httpd/assets/plexlogo.webp b/src/main/frontend/src/lib/assets/plexlogo.webp similarity index 100% rename from src/main/resources/httpd/assets/plexlogo.webp rename to src/main/frontend/src/lib/assets/plexlogo.webp diff --git a/src/main/frontend/src/lib/components/auth/StaffRequired.svelte b/src/main/frontend/src/lib/components/auth/StaffRequired.svelte new file mode 100644 index 0000000..652277e --- /dev/null +++ b/src/main/frontend/src/lib/components/auth/StaffRequired.svelte @@ -0,0 +1,25 @@ + + +{#if auth === null} +

Checking access...

+{:else} + +

Staff access required

+

You must sign in as staff to {action}.

+ {#if auth.reason !== 'disabled'} + + {/if} +
+{/if} diff --git a/src/main/frontend/src/lib/components/layout/AppShell.svelte b/src/main/frontend/src/lib/components/layout/AppShell.svelte new file mode 100644 index 0000000..b8db94c --- /dev/null +++ b/src/main/frontend/src/lib/components/layout/AppShell.svelte @@ -0,0 +1,139 @@ + + +
+
+
+ + + + +
+ {#if auth?.authenticated} + + + {:else if auth?.reason !== 'disabled'} + + {/if} + + +
+
+ + {#if menuOpen} + + {/if} +
+ +
+ {@render children?.()} +
+
diff --git a/src/main/frontend/src/lib/components/ui/InventoryGrid.svelte b/src/main/frontend/src/lib/components/ui/InventoryGrid.svelte new file mode 100644 index 0000000..f3c44dc --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/InventoryGrid.svelte @@ -0,0 +1,168 @@ + + +{#snippet slot(item: InventoryItem | null | undefined, key: string)} + {#if item} + {@const durability = durabilityPercent(item)} + + {:else} +
+ {/if} +{/snippet} + +{#if !inventory} +

Waiting for data...

+{:else if !inventory.online} +

Player is offline.

+{:else} +
+
+
+
+

Main

+
+
+ {#each inventory.storage ?? [] as item, index (index)} + {@render slot(item, `storage-${index}`)} + {/each} +
+
+ {#each inventory.hotbar ?? [] as item, index (index)} + {@render slot(item, `hotbar-${index}`)} + {/each} +
+
+
+
+
+

Armor

+
+ {@render slot(inventory.armor?.helmet, 'armor-helmet')} + {@render slot(inventory.armor?.chest, 'armor-chest')} + {@render slot(inventory.armor?.legs, 'armor-legs')} + {@render slot(inventory.armor?.boots, 'armor-boots')} +
+
+
+

Offhand

+ {@render slot(inventory.offhand, 'offhand')} +
+
+
+
+ +
+ {#if selectedItem} +
+
+
+ +
+
+ {#if selectedItem.name} +

{selectedItem.name}

+ {/if} +

{selectedItem.type}

+

Count: {selectedItem.amount}

+
+
+ + {#if selectedItem.lore?.length} +
+

Lore

+
    + {#each selectedItem.lore as line, index (index)} +
  • {line}
  • + {/each} +
+
+ {/if} + + {#if selectedItem.enchants} +
+

Enchantments

+
    + {#each Object.entries(selectedItem.enchants) as [key, value] (key)} +
  • {titleCase(key)}{ROMAN[value] || value}
  • + {/each} +
+
+ {/if} + + {#if selectedItem.nbt} +
+

NBT

+
{selectedItem.nbt}
+
+ {/if} +
+ {:else} +
+ Select an occupied slot to inspect the item. +
+ {/if} +
+
+{/if} diff --git a/src/main/frontend/src/lib/components/ui/ItemIcon.svelte b/src/main/frontend/src/lib/components/ui/ItemIcon.svelte new file mode 100644 index 0000000..278414f --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/ItemIcon.svelte @@ -0,0 +1,33 @@ + + +{#if url} + {titleCase(type)}/ +{:else} + + {normalized.replace(/_/g, ' ')} + +{/if} diff --git a/src/main/frontend/src/lib/components/ui/badge/badge.svelte b/src/main/frontend/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000..a99b94d --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,49 @@ + + + + + + {@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/badge/index.ts b/src/main/frontend/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000..d332eb4 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/badge/index.ts @@ -0,0 +1,2 @@ +export {default as Badge} from "./badge.svelte"; +export {badgeVariants, type BadgeVariant} from "./badge.svelte"; diff --git a/src/main/frontend/src/lib/components/ui/button/button.svelte b/src/main/frontend/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..1733b16 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/button/button.svelte @@ -0,0 +1,82 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/src/main/frontend/src/lib/components/ui/button/index.ts b/src/main/frontend/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..4fb817b --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants, +} from "./button.svelte"; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant, +}; diff --git a/src/main/frontend/src/lib/components/ui/card/card-action.svelte b/src/main/frontend/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000..a0a4833 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/card/card-content.svelte b/src/main/frontend/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..48a3d9e --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/card/card-description.svelte b/src/main/frontend/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..2fb1e84 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ + + +

+ {@render children?.()} +

diff --git a/src/main/frontend/src/lib/components/ui/card/card-footer.svelte b/src/main/frontend/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..042d3b6 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/card/card-header.svelte b/src/main/frontend/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..e65378b --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/card/card-title.svelte b/src/main/frontend/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..24b2f04 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/card/card.svelte b/src/main/frontend/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..5c00f28 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/card.svelte @@ -0,0 +1,22 @@ + + +
img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)} + {...restProps} +> + {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/card/index.ts b/src/main/frontend/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..19df11a --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/card/index.ts @@ -0,0 +1,25 @@ +import Root from "./card.svelte"; +import Content from "./card-content.svelte"; +import Description from "./card-description.svelte"; +import Footer from "./card-footer.svelte"; +import Header from "./card-header.svelte"; +import Title from "./card-title.svelte"; +import Action from "./card-action.svelte"; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + Action, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle, + Action as CardAction, +}; diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-close.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000..662c7ca --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-content.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..84039bd --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,49 @@ + + + + + + {@render children?.()} + {#if showCloseButton} + + {#snippet child({props})} + + {/snippet} + + {/if} + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-description.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..93cf59d --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-footer.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..791d15e --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,32 @@ + + +
+ {@render children?.()} + {#if showCloseButton} + + {#snippet child({props})} + + {/snippet} + + {/if} +
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-header.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..08c1939 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..76c134e --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-portal.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-portal.svelte new file mode 100644 index 0000000..7498c23 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-title.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..f53ced3 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000..3ced002 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/dialog.svelte b/src/main/frontend/src/lib/components/ui/dialog/dialog.svelte new file mode 100644 index 0000000..cbb8049 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/dialog.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dialog/index.ts b/src/main/frontend/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..29b3bb4 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,34 @@ +import Root from "./dialog.svelte"; +import Portal from "./dialog-portal.svelte"; +import Title from "./dialog-title.svelte"; +import Footer from "./dialog-footer.svelte"; +import Header from "./dialog-header.svelte"; +import Overlay from "./dialog-overlay.svelte"; +import Content from "./dialog-content.svelte"; +import Description from "./dialog-description.svelte"; +import Trigger from "./dialog-trigger.svelte"; +import Close from "./dialog-close.svelte"; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose, +}; diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte new file mode 100644 index 0000000..88c7a23 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000..da4f755 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte @@ -0,0 +1,45 @@ + + + + {#snippet children({checked, indeterminate})} + + {#if indeterminate} + + {:else if checked} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000..9b24a14 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte new file mode 100644 index 0000000..6efb520 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte new file mode 100644 index 0000000..334b96a --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000..59932d7 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000..155c7c2 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,24 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte new file mode 100644 index 0000000..d2bf4f1 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000..60dfe05 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000..bc1a3bf --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte @@ -0,0 +1,35 @@ + + + + {#snippet children({checked})} + + {#if checked} + + {/if} + + {@render childrenProp?.({checked})} + {/snippet} + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000..ed6d3f0 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000..0685d10 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000..deae797 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000..2996e9f --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,30 @@ + + + + {@render children?.()} + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte new file mode 100644 index 0000000..6993b5f --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte new file mode 100644 index 0000000..0aa29c5 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte new file mode 100644 index 0000000..9c32785 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/dropdown-menu/index.ts b/src/main/frontend/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..eca3d3f --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/dropdown-menu/index.ts @@ -0,0 +1,54 @@ +import Root from "./dropdown-menu.svelte"; +import Sub from "./dropdown-menu-sub.svelte"; +import CheckboxGroup from "./dropdown-menu-checkbox-group.svelte"; +import CheckboxItem from "./dropdown-menu-checkbox-item.svelte"; +import Content from "./dropdown-menu-content.svelte"; +import Group from "./dropdown-menu-group.svelte"; +import Item from "./dropdown-menu-item.svelte"; +import Label from "./dropdown-menu-label.svelte"; +import RadioGroup from "./dropdown-menu-radio-group.svelte"; +import RadioItem from "./dropdown-menu-radio-item.svelte"; +import Separator from "./dropdown-menu-separator.svelte"; +import Shortcut from "./dropdown-menu-shortcut.svelte"; +import Trigger from "./dropdown-menu-trigger.svelte"; +import SubContent from "./dropdown-menu-sub-content.svelte"; +import SubTrigger from "./dropdown-menu-sub-trigger.svelte"; +import GroupHeading from "./dropdown-menu-group-heading.svelte"; +import Portal from "./dropdown-menu-portal.svelte"; + +export { + CheckboxGroup, + CheckboxItem, + Content, + Portal, + Root as DropdownMenu, + CheckboxGroup as DropdownMenuCheckboxGroup, + CheckboxItem as DropdownMenuCheckboxItem, + Content as DropdownMenuContent, + Portal as DropdownMenuPortal, + Group as DropdownMenuGroup, + Item as DropdownMenuItem, + Label as DropdownMenuLabel, + RadioGroup as DropdownMenuRadioGroup, + RadioItem as DropdownMenuRadioItem, + Separator as DropdownMenuSeparator, + Shortcut as DropdownMenuShortcut, + Sub as DropdownMenuSub, + SubContent as DropdownMenuSubContent, + SubTrigger as DropdownMenuSubTrigger, + Trigger as DropdownMenuTrigger, + GroupHeading as DropdownMenuGroupHeading, + Group, + GroupHeading, + Item, + Label, + RadioGroup, + RadioItem, + Root, + Separator, + Shortcut, + Sub, + SubContent, + SubTrigger, + Trigger, +}; diff --git a/src/main/frontend/src/lib/components/ui/input/index.ts b/src/main/frontend/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..927b39f --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from "./input.svelte"; + +export { + Root, + // + Root as Input, +}; diff --git a/src/main/frontend/src/lib/components/ui/input/input.svelte b/src/main/frontend/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..fbc2c00 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/input/input.svelte @@ -0,0 +1,48 @@ + + +{#if type === "file"} + +{:else} + +{/if} diff --git a/src/main/frontend/src/lib/components/ui/label/index.ts b/src/main/frontend/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..d92d8e2 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from "./label.svelte"; + +export { + Root, + // + Root as Label, +}; diff --git a/src/main/frontend/src/lib/components/ui/label/label.svelte b/src/main/frontend/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..2e27c5d --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/label/label.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/select/index.ts b/src/main/frontend/src/lib/components/ui/select/index.ts new file mode 100644 index 0000000..ff2dde7 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/index.ts @@ -0,0 +1,37 @@ +import Root from "./select.svelte"; +import Group from "./select-group.svelte"; +import Label from "./select-label.svelte"; +import Item from "./select-item.svelte"; +import Content from "./select-content.svelte"; +import Trigger from "./select-trigger.svelte"; +import Separator from "./select-separator.svelte"; +import ScrollDownButton from "./select-scroll-down-button.svelte"; +import ScrollUpButton from "./select-scroll-up-button.svelte"; +import GroupHeading from "./select-group-heading.svelte"; +import Portal from "./select-portal.svelte"; + +export { + Root, + Group, + Label, + Item, + Content, + Trigger, + Separator, + ScrollDownButton, + ScrollUpButton, + GroupHeading, + Portal, + // + Root as Select, + Group as SelectGroup, + Label as SelectLabel, + Item as SelectItem, + Content as SelectContent, + Trigger as SelectTrigger, + Separator as SelectSeparator, + ScrollDownButton as SelectScrollDownButton, + ScrollUpButton as SelectScrollUpButton, + GroupHeading as SelectGroupHeading, + Portal as SelectPortal, +}; diff --git a/src/main/frontend/src/lib/components/ui/select/select-content.svelte b/src/main/frontend/src/lib/components/ui/select/select-content.svelte new file mode 100644 index 0000000..20ed6bc --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-content.svelte @@ -0,0 +1,45 @@ + + + + + + + {@render children?.()} + + + + diff --git a/src/main/frontend/src/lib/components/ui/select/select-group-heading.svelte b/src/main/frontend/src/lib/components/ui/select/select-group-heading.svelte new file mode 100644 index 0000000..2268718 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-group-heading.svelte @@ -0,0 +1,21 @@ + + + + {@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/select/select-group.svelte b/src/main/frontend/src/lib/components/ui/select/select-group.svelte new file mode 100644 index 0000000..4bb1485 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-group.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/select/select-item.svelte b/src/main/frontend/src/lib/components/ui/select/select-item.svelte new file mode 100644 index 0000000..1a08eb6 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-item.svelte @@ -0,0 +1,39 @@ + + + + {#snippet children({selected, highlighted})} + + {#if selected} + + {/if} + + {#if childrenProp} + {@render childrenProp({selected, highlighted})} + {:else} + {label || value} + {/if} + {/snippet} + diff --git a/src/main/frontend/src/lib/components/ui/select/select-label.svelte b/src/main/frontend/src/lib/components/ui/select/select-label.svelte new file mode 100644 index 0000000..2e37c1f --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-label.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/select/select-portal.svelte b/src/main/frontend/src/lib/components/ui/select/select-portal.svelte new file mode 100644 index 0000000..935b4eb --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte b/src/main/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte new file mode 100644 index 0000000..d189e85 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-scroll-down-button.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/main/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte b/src/main/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte new file mode 100644 index 0000000..a131126 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-scroll-up-button.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/main/frontend/src/lib/components/ui/select/select-separator.svelte b/src/main/frontend/src/lib/components/ui/select/select-separator.svelte new file mode 100644 index 0000000..0d3130d --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-separator.svelte @@ -0,0 +1,18 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/select/select-trigger.svelte b/src/main/frontend/src/lib/components/ui/select/select-trigger.svelte new file mode 100644 index 0000000..b315811 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select-trigger.svelte @@ -0,0 +1,30 @@ + + + + {@render children?.()} + + diff --git a/src/main/frontend/src/lib/components/ui/select/select.svelte b/src/main/frontend/src/lib/components/ui/select/select.svelte new file mode 100644 index 0000000..44b1d43 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/select/select.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/separator/index.ts b/src/main/frontend/src/lib/components/ui/separator/index.ts new file mode 100644 index 0000000..98a029d --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from "./separator.svelte"; + +export { + Root, + // + Root as Separator, +}; diff --git a/src/main/frontend/src/lib/components/ui/separator/separator.svelte b/src/main/frontend/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 0000000..4cb260d --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,23 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/index.ts b/src/main/frontend/src/lib/components/ui/sheet/index.ts new file mode 100644 index 0000000..2f04a4f --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/index.ts @@ -0,0 +1,34 @@ +import Root from "./sheet.svelte"; +import Portal from "./sheet-portal.svelte"; +import Trigger from "./sheet-trigger.svelte"; +import Close from "./sheet-close.svelte"; +import Overlay from "./sheet-overlay.svelte"; +import Content from "./sheet-content.svelte"; +import Header from "./sheet-header.svelte"; +import Footer from "./sheet-footer.svelte"; +import Title from "./sheet-title.svelte"; +import Description from "./sheet-description.svelte"; + +export { + Root, + Close, + Trigger, + Portal, + Overlay, + Content, + Header, + Footer, + Title, + Description, + // + Root as Sheet, + Close as SheetClose, + Trigger as SheetTrigger, + Portal as SheetPortal, + Overlay as SheetOverlay, + Content as SheetContent, + Header as SheetHeader, + Footer as SheetFooter, + Title as SheetTitle, + Description as SheetDescription, +}; diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-close.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-close.svelte new file mode 100644 index 0000000..d40b600 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-content.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-content.svelte new file mode 100644 index 0000000..2c63f5b --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-content.svelte @@ -0,0 +1,56 @@ + + + + + + + + {@render children?.()} + {#if showCloseButton} + + {#snippet child({props})} + + {/snippet} + + {/if} + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-description.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-description.svelte new file mode 100644 index 0000000..f402a46 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-footer.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-footer.svelte new file mode 100644 index 0000000..d072545 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-header.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-header.svelte new file mode 100644 index 0000000..1cecfb2 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte new file mode 100644 index 0000000..559b78a --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-portal.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-portal.svelte new file mode 100644 index 0000000..18b27de --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-title.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-title.svelte new file mode 100644 index 0000000..935cb54 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte new file mode 100644 index 0000000..e2f4995 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/sheet/sheet.svelte b/src/main/frontend/src/lib/components/ui/sheet/sheet.svelte new file mode 100644 index 0000000..caa0e10 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/sheet/sheet.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/main/frontend/src/lib/components/ui/table/index.ts b/src/main/frontend/src/lib/components/ui/table/index.ts new file mode 100644 index 0000000..9296e83 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/index.ts @@ -0,0 +1,28 @@ +import Root from "./table.svelte"; +import Body from "./table-body.svelte"; +import Caption from "./table-caption.svelte"; +import Cell from "./table-cell.svelte"; +import Footer from "./table-footer.svelte"; +import Head from "./table-head.svelte"; +import Header from "./table-header.svelte"; +import Row from "./table-row.svelte"; + +export { + Root, + Body, + Caption, + Cell, + Footer, + Head, + Header, + Row, + // + Root as Table, + Body as TableBody, + Caption as TableCaption, + Cell as TableCell, + Footer as TableFooter, + Head as TableHead, + Header as TableHeader, + Row as TableRow, +}; diff --git a/src/main/frontend/src/lib/components/ui/table/table-body.svelte b/src/main/frontend/src/lib/components/ui/table/table-body.svelte new file mode 100644 index 0000000..e464270 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table-body.svelte @@ -0,0 +1,15 @@ + + + +{@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/table/table-caption.svelte b/src/main/frontend/src/lib/components/ui/table/table-caption.svelte new file mode 100644 index 0000000..e8cee36 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table-caption.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/table/table-cell.svelte b/src/main/frontend/src/lib/components/ui/table/table-cell.svelte new file mode 100644 index 0000000..62a122d --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table-cell.svelte @@ -0,0 +1,16 @@ + + + + {@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/table/table-footer.svelte b/src/main/frontend/src/lib/components/ui/table/table-footer.svelte new file mode 100644 index 0000000..86c8433 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table-footer.svelte @@ -0,0 +1,20 @@ + + +tr]:last:border-b-0", className)} + {...restProps} +> +{@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/table/table-head.svelte b/src/main/frontend/src/lib/components/ui/table/table-head.svelte new file mode 100644 index 0000000..fcb642b --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table-head.svelte @@ -0,0 +1,17 @@ + + + + {@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/table/table-header.svelte b/src/main/frontend/src/lib/components/ui/table/table-header.svelte new file mode 100644 index 0000000..ac6003c --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table-header.svelte @@ -0,0 +1,20 @@ + + + +{@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/table/table-row.svelte b/src/main/frontend/src/lib/components/ui/table/table-row.svelte new file mode 100644 index 0000000..fb4a504 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table-row.svelte @@ -0,0 +1,16 @@ + + + + {@render children?.()} + diff --git a/src/main/frontend/src/lib/components/ui/table/table.svelte b/src/main/frontend/src/lib/components/ui/table/table.svelte new file mode 100644 index 0000000..dbd3197 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/table/table.svelte @@ -0,0 +1,17 @@ + + +
+ + {@render children?.()} +
+
diff --git a/src/main/frontend/src/lib/components/ui/textarea/index.ts b/src/main/frontend/src/lib/components/ui/textarea/index.ts new file mode 100644 index 0000000..133f780 --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/textarea/index.ts @@ -0,0 +1,7 @@ +import Root from "./textarea.svelte"; + +export { + Root, + // + Root as Textarea, +}; diff --git a/src/main/frontend/src/lib/components/ui/textarea/textarea.svelte b/src/main/frontend/src/lib/components/ui/textarea/textarea.svelte new file mode 100644 index 0000000..522eefb --- /dev/null +++ b/src/main/frontend/src/lib/components/ui/textarea/textarea.svelte @@ -0,0 +1,23 @@ + + + diff --git a/src/main/frontend/src/lib/pages/CommandsPage.svelte b/src/main/frontend/src/lib/pages/CommandsPage.svelte new file mode 100644 index 0000000..f89e9db --- /dev/null +++ b/src/main/frontend/src/lib/pages/CommandsPage.svelte @@ -0,0 +1,97 @@ + + +
+

Commands

+
+ +
+
+ + +
+ +
+ +{#if loading} +

Loading commands...

+{:else if error} +

{error}

+{:else if visibleGroups.length === 0} +

No commands match that filter.

+{:else} +
+ {#each visibleGroups as group, groupIndex (`${group.plugin}:${groupIndex}`)} + +
+ + {group.plugin} + {group.commands.length} + commands + +
+ {#each group.commands as command, commandIndex (`${command.name}:${commandIndex}`)} +
+
+

+ /{command.name}

+ {#if command.aliases?.length} +

{command.aliases.map((alias) => `/${alias}`).join(', ')}

+ {/if} +
+
+

{command.description || 'No description.'}

+ {#if command.usage} +

{command.usage}

+ {/if} + {#if command.permission} +

{command.permission}

+ {/if} +
+
+ {/each} +
+
+
+ {/each} +
+{/if} diff --git a/src/main/frontend/src/lib/pages/HomePage.svelte b/src/main/frontend/src/lib/pages/HomePage.svelte new file mode 100644 index 0000000..7a9cee9 --- /dev/null +++ b/src/main/frontend/src/lib/pages/HomePage.svelte @@ -0,0 +1,203 @@ + + +
+
+

Overview

+

Minecraft version {stats?.server.version ?? '-'}

+
+
+ +
+ +
+ Players + +
+
+ {stats?.players.online ?? '-'} + / {stats?.players.max ?? '-'} +
+
+
+
+ view list +
+ + +
+ CPU + +
+
{pct(stats?.cpu.process)}
+
+
+
+
+ {stats?.cpu.cores ?? '-'} cores + system {pct(stats?.cpu.system)} +
+
+ + +
+ Memory + +
+
+ {formatBytes(stats?.memory.used).split(' ')[0]} + {formatBytes(stats?.memory.used).split(' ')[1] ?? ''} +
+
+
+
+
+ {memoryPercent ? memoryPercent.toFixed(1) : '-'}% + max {formatBytes(stats?.memory.max)} +
+
+ + +
+ Ticks per second + +
+
+ {tpsText(tps[0])} + / 20.00 +
+ + + +
+ 5m {tpsText(tps[1])} + 15m {tpsText(tps[2])} +
+
+
+ +
+ +
+ Uptime + +
+
{uptime}
+
+ + +
+ World + +
+
+
+
Worlds
+
{stats?.world.worlds ?? '-'}
+
+
+
Chunks
+
{stats?.world.loadedChunks ?? '-'}
+
+
+
Entities
+
{stats?.world.entities ?? '-'}
+
+
+
+ + +
+ Plugins + +
+
+ {stats?.plugins.active ?? '-'} + active +
+ +
+
diff --git a/src/main/frontend/src/lib/pages/IndefBansPage.svelte b/src/main/frontend/src/lib/pages/IndefBansPage.svelte new file mode 100644 index 0000000..e00053d --- /dev/null +++ b/src/main/frontend/src/lib/pages/IndefBansPage.svelte @@ -0,0 +1,156 @@ + + +
+

Indefinite bans

+
+ {totals.groups} groups + {totals.users} users + {totals.uuids} uuids + {totals.ips} ips +
+
+ +
+
+ + +
+
+ +{#if loading} +

Loading bans...

+{:else if error} +

{error}

+{:else if groups.length === 0} + +

No indefinite bans configured.

+
+{:else if visible.length === 0} +

No indefinite bans match that filter.

+{:else} +
+ {#each visible as group, index (groupKey(group, index))} + {@const total = entryCount(group)} + +
+

+ {#if group.reason} + {group.reason} + {:else} + No reason provided + {/if} +

+ {total} {total === 1 ? 'entry' : 'entries'} +
+
+ {#if group.usernames.length} +
Users
+
+ {#each group.usernames as username, usernameIndex (`${username}:${usernameIndex}`)} + {username} + {/each} +
+ {/if} + {#if group.uuids.length} +
UUIDs
+
+ {#each group.uuids as uuid, uuidIndex (`${uuid}:${uuidIndex}`)} + {uuid} + {/each} +
+ {/if} + {#if group.ips.length} +
IPs
+
+ {#each group.ips as ip, ipIndex (`${ip}:${ipIndex}`)} + {ip} + {/each} +
+ {/if} +
+
+ {/each} +
+{/if} diff --git a/src/main/frontend/src/lib/pages/PlayerPage.svelte b/src/main/frontend/src/lib/pages/PlayerPage.svelte new file mode 100644 index 0000000..c3e8864 --- /dev/null +++ b/src/main/frontend/src/lib/pages/PlayerPage.svelte @@ -0,0 +1,273 @@ + + +{#if loading} +

Loading player...

+{:else if error} + +

Player lookup failed

+

{error}

+
+{:else if player} +
+
+ +
+

{player.name}

+

{player.uuid}

+
+
+ +
+ +
+ +

Info

+
+
Status
+
+ {#if online} + online + {:else} + offline + {/if} +
+
Ping
+
{online ? `${online.ping | 0}ms` : '-'}
+
World
+
{online?.world ?? '-'}
+
Gamemode
+
{online?.gamemode ? titleCase(online.gamemode) : '-'}
+
IP
+
{player.ip ?? '-'}
+
First played
+
{player.firstPlayed ?? '-'}
+
Punishments
+
View history
+ {#if player.nameMcUrl} +
NameMC
+
View profile + +
+ {/if} +
+
+ + +

Actions

+

Issued punishments use the authenticated staff account.

+
+ {#each actions as item (item.action)} + + {/each} +
+ {#if actionMessage} +

{actionMessage}

+ {/if} +
+
+ + {#if staff} +
+ +

Live inventory

+
+ (selectedSlot = slot)}/> +
+
+
+ {/if} + + {#if activeAction} + + + + Confirm {activeAction.label.toLowerCase()} + + Target: {player.name}{'selected' in activeAction && activeAction.selected ? ` | Slot: ${selectedSlot}` : ''} + + + {#if activeAction.reason} +
+ +