Add CSS via SCSS to the frontend build pipeline

This commit is contained in:
Eli Ribble 2026-03-21 19:14:51 +00:00
parent 1e67c0090d
commit f3c818a48f
No known key found for this signature in database
12 changed files with 924 additions and 94 deletions

View file

@ -1,5 +1,6 @@
import esbuild from "esbuild"; import esbuild from "esbuild";
import vue from "esbuild-plugin-vue3"; import vue from "esbuild-plugin-vue3";
import { sassPlugin } from "esbuild-sass-plugin";
const args = process.argv.slice(2); const args = process.argv.slice(2);
const watch = args.includes("--watch"); const watch = args.includes("--watch");
@ -9,7 +10,14 @@ const config = {
entryPoints: ["ts/main.ts"], entryPoints: ["ts/main.ts"],
bundle: true, bundle: true,
format: "esm", format: "esm",
plugins: [vue()], plugins: [
sassPlugin({
quietDeps: true,
silenceDeprecations: ["import"], // silence known issue with Bootstrap #40962
type: "css",
}),
vue(),
],
define: { define: {
__VUE_OPTIONS_API__: "true", __VUE_OPTIONS_API__: "true",
__VUE_PROD_DEVTOOLS__: "false", __VUE_PROD_DEVTOOLS__: "false",

View file

@ -39,21 +39,21 @@ pnpm config set store-dir $HOME/.pnpm-store
# Compile SCSS # Compile SCSS
SASS_SRC_DIR="./scss" SASS_SRC_DIR="./scss"
CSS_OUTPUT_DIR="./static/gen/css" GEN_OUTPUT_DIR="./static/gen"
mkdir -p "$CSS_OUTPUT_DIR" mkdir -p "$GEN_OUTPUT_DIR"
echo "Compiling $SASS_SRC_DIR/style.scss to $CSS_OUTPUT_DIR/style.css..." echo "Compiling $SASS_SRC_DIR/style.scss to $GEN_OUTPUT_DIR/main.css..."
sass --style=compressed --trace "$SASS_SRC_DIR/style.scss":"$CSS_OUTPUT_DIR/style.css" sass --style=compressed --trace "$SASS_SRC_DIR/style.scss":"$GEN_OUTPUT_DIR/main.css"
# Generate hash and rename style # Generate hash and rename style
STYLE_HASH=$(sha256sum "$CSS_OUTPUT_DIR/style.css" | cut -c1-12) STYLE_HASH=$(sha256sum "$GEN_OUTPUT_DIR/main.css" | cut -c1-12)
mv "$CSS_OUTPUT_DIR/style.css" "$CSS_OUTPUT_DIR/style.$STYLE_HASH.css" mv "$GEN_OUTPUT_DIR/main.css" "$GEN_OUTPUT_DIR/style.$STYLE_HASH.css"
echo "Generated CSS style with hash: $STYLE_HASH" echo "Generated CSS style with hash: $STYLE_HASH"
# Bundle TypeScript # Bundle TypeScript
JS_OUTPUT_DIR="./static/gen/js" GEN_OUTPUT_DIR="./static/gen"
mkdir -p "$JS_OUTPUT_DIR" mkdir -p "$GEN_OUTPUT_DIR"
echo "Bundling TypeScript with Vue..." echo "Bundling TypeScript with Vue..."
esbuild ts/main.ts \ esbuild ts/main.ts \
@ -64,11 +64,11 @@ esbuild ts/main.ts \
--define:__VUE_PROD_DEVTOOLS__=false \ --define:__VUE_PROD_DEVTOOLS__=false \
--define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false \ --define:__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false \
--alias:vue=vue/dist/vue.esm-bundler.js \ --alias:vue=vue/dist/vue.esm-bundler.js \
--outfile="$JS_OUTPUT_DIR/bundle.js" --outfile="$GEN_OUTPUT_DIR/main.js"
# Generate hash and rename bundle # Generate hash and rename bundle
BUNDLE_HASH=$(sha256sum "$JS_OUTPUT_DIR/bundle.js" | cut -c1-12) BUNDLE_HASH=$(sha256sum "$GEN_OUTPUT_DIR/main.js" | cut -c1-12)
mv "$JS_OUTPUT_DIR/bundle.js" "$JS_OUTPUT_DIR/bundle.$BUNDLE_HASH.js" mv "$GEN_OUTPUT_DIR/main.js" "$GEN_OUTPUT_DIR/bundle.$BUNDLE_HASH.js"
echo "Generated JS bundle with hash: $BUNDLE_HASH" echo "Generated JS bundle with hash: $BUNDLE_HASH"
# Generate gen.go with bundle path # Generate gen.go with bundle path

View file

@ -12,72 +12,6 @@
{{ block "extraheader" . }}{{ end }} {{ block "extraheader" . }}{{ end }}
<script> <script>
const USER = {{ .User.AsJSON|json }}; const USER = {{ .User.AsJSON|json }};
document.addEventListener("DOMContentLoaded", () => {
SSEManager.subscribe("*", function (e) {
if (e.type != "heartbeat") {
updateUserState();
}
});
});
document.addEventListener("alpine:init", () => {
Alpine.store("user", USER);
})
function restoreLocalStorage() {
const expanded = localStorage.getItem("sidebar.expanded");
if (expanded == "false") {
document.getElementById("sidebar").classList.add("collapsed");
document.getElementById("content").classList.add("expanded");
} else {
document.getElementById("sidebar").classList.remove("collapsed");
document.getElementById("content").classList.remove("expanded");
localStorage.setItem("sidebar.expanded", "true");
}
}
function setTooltipsForSidebar() {
const sidebarTooltips = document.querySelectorAll(
'#sidebar [data-bs-toggle="tooltip"]',
);
const isExpanded = document
.getElementById("content")
.classList.contains("expanded");
sidebarTooltips.forEach((t) => {
const tooltip = bootstrap.Tooltip.getOrCreateInstance(t);
if (isExpanded) {
tooltip.enable();
} else {
tooltip.disable();
}
});
}
async function updateUserState() {
const response = await fetch("/api/user/self");
const data = await response.json();
// Update properties instead of replacing the whole store which leverages Alpine's reactivity
const store_user = Alpine.store("user");
Object.keys(data).forEach(key => {
store_user[key] = data[key];
});
}
document.addEventListener("DOMContentLoaded", function () {
var popoverTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="popover"]'),
);
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl);
});
console.log("Initialized ", popoverTriggerList.length, " popovers");
var tooltipTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="tooltip"]'),
);
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
let t = new bootstrap.Tooltip(tooltipTriggerEl);
return t;
});
console.log("Initialized ", tooltipTriggerList.length, " tooltips");
restoreLocalStorage();
setTooltipsForSidebar();
});
</script> {{ if not .Config.IsProductionEnvironment }} </script> {{ if not .Config.IsProductionEnvironment }}
<script src="/.flogo/injector.js"></script> <script src="/.flogo/injector.js"></script>
{{ end }} {{ end }}
@ -94,17 +28,5 @@
{{ template "content" . }} {{ template "content" . }}
</div> </div>
<div id="flogo"></div> <div id="flogo"></div>
<script>
document.getElementById("sidebarToggle").addEventListener("click", () => {
const sidebar = document.getElementById("sidebar");
sidebar.classList.toggle("collapsed");
document.getElementById("content").classList.toggle("expanded");
setTooltipsForSidebar();
localStorage.setItem(
"sidebar.expanded",
(!sidebar.classList.contains("collapsed")).toString(),
);
});
</script>
</body> </body>
</html> </html>

View file

@ -4,12 +4,17 @@
"private": true, "private": true,
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.1",
"maplibre-gl": "^5.21.0", "maplibre-gl": "^5.21.0",
"vue": "^3.5.30" "vue": "^3.5.30"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "^0.25.5", "esbuild": "^0.25.5",
"esbuild-plugin-vue3": "^0.5.1", "esbuild-plugin-vue3": "^0.5.1",
"esbuild-sass-plugin": "^3.7.0",
"sass": "^1.98.0",
"typescript": "^5.9.3" "typescript": "^5.9.3"
}, },
"scripts": { "scripts": {

558
pnpm-lock.yaml generated
View file

@ -8,6 +8,15 @@ importers:
.: .:
dependencies: dependencies:
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
bootstrap:
specifier: ^5.3.8
version: 5.3.8(@popperjs/core@2.11.8)
bootstrap-icons:
specifier: ^1.13.1
version: 1.13.1
maplibre-gl: maplibre-gl:
specifier: ^5.21.0 specifier: ^5.21.0
version: 5.21.0 version: 5.21.0
@ -20,7 +29,13 @@ importers:
version: 0.25.12 version: 0.25.12
esbuild-plugin-vue3: esbuild-plugin-vue3:
specifier: ^0.5.1 specifier: ^0.5.1
version: 0.5.1(vue@3.5.30(typescript@5.9.3)) version: 0.5.1(sass@1.98.0)(vue@3.5.30(typescript@5.9.3))
esbuild-sass-plugin:
specifier: ^3.7.0
version: 3.7.0(esbuild@0.25.12)(sass-embedded@1.98.0)
sass:
specifier: ^1.98.0
version: 1.98.0
typescript: typescript:
specifier: ^5.9.3 specifier: ^5.9.3
version: 5.9.3 version: 5.9.3
@ -44,6 +59,9 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@bufbuild/protobuf@2.11.0':
resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==}
'@esbuild/aix-ppc64@0.25.12': '@esbuild/aix-ppc64@0.25.12':
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -239,6 +257,91 @@ packages:
'@maplibre/vt-pbf@4.3.0': '@maplibre/vt-pbf@4.3.0':
resolution: {integrity: sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==} resolution: {integrity: sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==}
'@parcel/watcher-android-arm64@2.5.6':
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [android]
'@parcel/watcher-darwin-arm64@2.5.6':
resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [darwin]
'@parcel/watcher-darwin-x64@2.5.6':
resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [darwin]
'@parcel/watcher-freebsd-x64@2.5.6':
resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [freebsd]
'@parcel/watcher-linux-arm-glibc@2.5.6':
resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
'@parcel/watcher-linux-arm-musl@2.5.6':
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
'@parcel/watcher-linux-arm64-glibc@2.5.6':
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-arm64-musl@2.5.6':
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-x64-glibc@2.5.6':
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
'@parcel/watcher-linux-x64-musl@2.5.6':
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
'@parcel/watcher-win32-arm64@2.5.6':
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [win32]
'@parcel/watcher-win32-ia32@2.5.6':
resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==}
engines: {node: '>= 10.0.0'}
cpu: [ia32]
os: [win32]
'@parcel/watcher-win32-x64@2.5.6':
resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [win32]
'@parcel/watcher@2.5.6':
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
'@types/geojson@7946.0.16': '@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
@ -274,9 +377,28 @@ packages:
'@vue/shared@3.5.30': '@vue/shared@3.5.30':
resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==} resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==}
bootstrap-icons@1.13.1:
resolution: {integrity: sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==}
bootstrap@5.3.8:
resolution: {integrity: sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==}
peerDependencies:
'@popperjs/core': ^2.11.8
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
colorjs.io@0.5.2:
resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==}
csstype@3.2.3: csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
earcut@3.0.2: earcut@3.0.2:
resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==} resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==}
@ -302,6 +424,12 @@ packages:
sass: sass:
optional: true optional: true
esbuild-sass-plugin@3.7.0:
resolution: {integrity: sha512-vxNSXFx3/0ZFApKo9036ek2iRfsT+yVO99qIYqa+JaDSuJuId2/N4s1TY+xfK+5LRpAMQkfdBVUTxb/1r2bq1A==}
peerDependencies:
esbuild: '>=0.27.3'
sass-embedded: ^1.97.3
esbuild@0.25.12: esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -310,9 +438,35 @@ packages:
estree-walker@2.0.2: estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
gl-matrix@3.4.4: gl-matrix@3.4.4:
resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==} resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
immutable@5.1.5:
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
json-stringify-pretty-compact@4.0.0: json-stringify-pretty-compact@4.0.0:
resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==} resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==}
@ -337,6 +491,12 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
pbf@4.0.1: pbf@4.0.1:
resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==} resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==}
hasBin: true hasBin: true
@ -344,6 +504,10 @@ packages:
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
postcss@8.5.8: postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
@ -357,12 +521,138 @@ packages:
quickselect@3.0.0: quickselect@3.0.0:
resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==} resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==}
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
resolve-protobuf-schema@2.1.0: resolve-protobuf-schema@2.1.0:
resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==} resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==}
resolve@1.22.11:
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
engines: {node: '>= 0.4'}
hasBin: true
rw@1.3.3: rw@1.3.3:
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
sass-embedded-all-unknown@1.98.0:
resolution: {integrity: sha512-6n4RyK7/1mhdfYvpP3CClS3fGoYqDvRmLClCESS6I7+SAzqjxvGG6u5Fo+cb1nrPNbbilgbM4QKdgcgWHO9NCA==}
cpu: ['!arm', '!arm64', '!riscv64', '!x64']
sass-embedded-android-arm64@1.98.0:
resolution: {integrity: sha512-M9Ra98A6vYJHpwhoC/5EuH1eOshQ9ZyNwC8XifUDSbRl/cGeQceT1NReR9wFj3L7s1pIbmes1vMmaY2np0uAKQ==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [android]
sass-embedded-android-arm@1.98.0:
resolution: {integrity: sha512-LjGiMhHgu7VL1n7EJxTCre1x14bUsWd9d3dnkS2rku003IWOI/fxc7OXgaKagoVzok1kv09rzO3vFXJR5ZeONQ==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [android]
sass-embedded-android-riscv64@1.98.0:
resolution: {integrity: sha512-WPe+0NbaJIZE1fq/RfCZANMeIgmy83x4f+SvFOG7LhUthHpZWcOcrPTsCKKmN3xMT3iw+4DXvqTYOCYGRL3hcQ==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [android]
sass-embedded-android-x64@1.98.0:
resolution: {integrity: sha512-zrD25dT7OHPEgLWuPEByybnIfx4rnCtfge4clBgjZdZ3lF6E7qNLRBtSBmoFflh6Vg0RlEjJo5VlpnTMBM5MQQ==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [android]
sass-embedded-darwin-arm64@1.98.0:
resolution: {integrity: sha512-cgr1z9rBnCdMf8K+JabIaYd9Rag2OJi5mjq08XJfbJGMZV/TA6hFJCLGkr5/+ZOn4/geTM5/3aSfQ8z5EIJAOg==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [darwin]
sass-embedded-darwin-x64@1.98.0:
resolution: {integrity: sha512-OLBOCs/NPeiMqTdOrMFbVHBQFj19GS3bSVSxIhcCq16ZyhouUkYJEZjxQgzv9SWA2q6Ki8GCqp4k6jMeUY9dcA==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [darwin]
sass-embedded-linux-arm64@1.98.0:
resolution: {integrity: sha512-axOE3t2MTBwCtkUCbrdM++Gj0gC0fdHJPrgzQ+q1WUmY9NoNMGqflBtk5mBZaWUeha2qYO3FawxCB8lctFwCtw==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [linux]
sass-embedded-linux-arm@1.98.0:
resolution: {integrity: sha512-03baQZCxVyEp8v1NWBRlzGYrmVT/LK7ZrHlF1piscGiGxwfdxoLXVuxsylx3qn/dD/4i/rh7Bzk7reK1br9jvQ==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [linux]
sass-embedded-linux-musl-arm64@1.98.0:
resolution: {integrity: sha512-LeqNxQA8y4opjhe68CcFvMzCSrBuJqYVFbwElEj9bagHXQHTp9xVPJRn6VcrC+0VLEDq13HVXMv7RslIuU0zmA==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [linux]
sass-embedded-linux-musl-arm@1.98.0:
resolution: {integrity: sha512-OBkjTDPYR4hSaueOGIM6FDpl9nt/VZwbSRpbNu9/eEJcxE8G/vynRugW8KRZmCFjPy8j/jkGBvvS+k9iOqKV3g==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [linux]
sass-embedded-linux-musl-riscv64@1.98.0:
resolution: {integrity: sha512-7w6hSuOHKt8FZsmjRb3iGSxEzM87fO9+M8nt5JIQYMhHTj5C+JY/vcske0v715HCVj5e1xyTnbGXf8FcASeAIw==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [linux]
sass-embedded-linux-musl-x64@1.98.0:
resolution: {integrity: sha512-QikNyDEJOVqPmxyCFkci8ZdCwEssdItfjQFJB+D+Uy5HFqcS5Lv3d3GxWNX/h1dSb23RPyQdQc267ok5SbEyJw==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [linux]
sass-embedded-linux-riscv64@1.98.0:
resolution: {integrity: sha512-E7fNytc/v4xFBQKzgzBddV/jretA4ULAPO6XmtBiQu4zZBdBozuSxsQLe2+XXeb0X4S2GIl72V7IPABdqke/vA==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [linux]
sass-embedded-linux-x64@1.98.0:
resolution: {integrity: sha512-VsvP0t/uw00mMNPv3vwyYKUrFbqzxQHnRMO+bHdAMjvLw4NFf6mscpym9Bzf+NXwi1ZNKnB6DtXjmcpcvqFqYg==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [linux]
sass-embedded-unknown-all@1.98.0:
resolution: {integrity: sha512-C4MMzcAo3oEDQnW7L8SBgB9F2Fq5qHPnaYTZRMOH3Mp/7kM4OooBInXpCiiFjLnjY95hzP4KyctVx0uYR6MYlQ==}
os: ['!android', '!darwin', '!linux', '!win32']
sass-embedded-win32-arm64@1.98.0:
resolution: {integrity: sha512-nP/10xbAiPbhQkMr3zQfXE4TuOxPzWRQe1Hgbi90jv2R4TbzbqQTuZVOaJf7KOAN4L2Bo6XCTRjK5XkVnwZuwQ==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [win32]
sass-embedded-win32-x64@1.98.0:
resolution: {integrity: sha512-/lbrVsfbcbdZQ5SJCWcV0NVPd6YRs+FtAnfedp4WbCkO/ZO7Zt/58MvI4X2BVpRY/Nt5ZBo1/7v2gYcQ+J4svQ==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [win32]
sass-embedded@1.98.0:
resolution: {integrity: sha512-Do7u6iRb6K+lrllcTkB1BXcHwOxcKe3rEfOF/GcCLE2w3WpddakRAosJOHFUR37DpsvimQXEt5abs3NzUjEIqg==}
engines: {node: '>=16.0.0'}
hasBin: true
sass@1.98.0:
resolution: {integrity: sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==}
engines: {node: '>=14.0.0'}
hasBin: true
source-map-js@1.2.1: source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -370,9 +660,28 @@ packages:
supercluster@8.0.1: supercluster@8.0.1:
resolution: {integrity: sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==} resolution: {integrity: sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==}
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
sync-child-process@1.0.2:
resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==}
engines: {node: '>=16.0.0'}
sync-message-port@1.2.0:
resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==}
engines: {node: '>=16.0.0'}
tinyqueue@3.0.0: tinyqueue@3.0.0:
resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
typescript@4.9.5: typescript@4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'} engines: {node: '>=4.2.0'}
@ -383,6 +692,9 @@ packages:
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
varint@6.0.0:
resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
vue@3.5.30: vue@3.5.30:
resolution: {integrity: sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==} resolution: {integrity: sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==}
peerDependencies: peerDependencies:
@ -406,6 +718,8 @@ snapshots:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5 '@babel/helper-validator-identifier': 7.28.5
'@bufbuild/protobuf@2.11.0': {}
'@esbuild/aix-ppc64@0.25.12': '@esbuild/aix-ppc64@0.25.12':
optional: true optional: true
@ -532,6 +846,69 @@ snapshots:
pbf: 4.0.1 pbf: 4.0.1
supercluster: 8.0.1 supercluster: 8.0.1
'@parcel/watcher-android-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-x64@2.5.6':
optional: true
'@parcel/watcher-freebsd-x64@2.5.6':
optional: true
'@parcel/watcher-linux-arm-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm-musl@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-musl@2.5.6':
optional: true
'@parcel/watcher-linux-x64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-x64-musl@2.5.6':
optional: true
'@parcel/watcher-win32-arm64@2.5.6':
optional: true
'@parcel/watcher-win32-ia32@2.5.6':
optional: true
'@parcel/watcher-win32-x64@2.5.6':
optional: true
'@parcel/watcher@2.5.6':
dependencies:
detect-libc: 2.1.2
is-glob: 4.0.3
node-addon-api: 7.1.1
picomatch: 4.0.3
optionalDependencies:
'@parcel/watcher-android-arm64': 2.5.6
'@parcel/watcher-darwin-arm64': 2.5.6
'@parcel/watcher-darwin-x64': 2.5.6
'@parcel/watcher-freebsd-x64': 2.5.6
'@parcel/watcher-linux-arm-glibc': 2.5.6
'@parcel/watcher-linux-arm-musl': 2.5.6
'@parcel/watcher-linux-arm64-glibc': 2.5.6
'@parcel/watcher-linux-arm64-musl': 2.5.6
'@parcel/watcher-linux-x64-glibc': 2.5.6
'@parcel/watcher-linux-x64-musl': 2.5.6
'@parcel/watcher-win32-arm64': 2.5.6
'@parcel/watcher-win32-ia32': 2.5.6
'@parcel/watcher-win32-x64': 2.5.6
optional: true
'@popperjs/core@2.11.8': {}
'@types/geojson@7946.0.16': {} '@types/geojson@7946.0.16': {}
'@types/supercluster@7.1.3': '@types/supercluster@7.1.3':
@ -592,16 +969,40 @@ snapshots:
'@vue/shared@3.5.30': {} '@vue/shared@3.5.30': {}
bootstrap-icons@1.13.1: {}
bootstrap@5.3.8(@popperjs/core@2.11.8):
dependencies:
'@popperjs/core': 2.11.8
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
colorjs.io@0.5.2: {}
csstype@3.2.3: {} csstype@3.2.3: {}
detect-libc@2.1.2:
optional: true
earcut@3.0.2: {} earcut@3.0.2: {}
entities@7.0.1: {} entities@7.0.1: {}
esbuild-plugin-vue3@0.5.1(vue@3.5.30(typescript@5.9.3)): esbuild-plugin-vue3@0.5.1(sass@1.98.0)(vue@3.5.30(typescript@5.9.3)):
dependencies: dependencies:
typescript: 4.9.5 typescript: 4.9.5
vue: 3.5.30(typescript@5.9.3) vue: 3.5.30(typescript@5.9.3)
optionalDependencies:
sass: 1.98.0
esbuild-sass-plugin@3.7.0(esbuild@0.25.12)(sass-embedded@1.98.0):
dependencies:
esbuild: 0.25.12
resolve: 1.22.11
sass: 1.98.0
sass-embedded: 1.98.0
esbuild@0.25.12: esbuild@0.25.12:
optionalDependencies: optionalDependencies:
@ -634,8 +1035,30 @@ snapshots:
estree-walker@2.0.2: {} estree-walker@2.0.2: {}
function-bind@1.1.2: {}
gl-matrix@3.4.4: {} gl-matrix@3.4.4: {}
has-flag@4.0.0: {}
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
immutable@5.1.5: {}
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
is-extglob@2.1.1:
optional: true
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
optional: true
json-stringify-pretty-compact@4.0.0: {} json-stringify-pretty-compact@4.0.0: {}
kdbush@4.0.2: {} kdbush@4.0.2: {}
@ -672,12 +1095,20 @@ snapshots:
nanoid@3.3.11: {} nanoid@3.3.11: {}
node-addon-api@7.1.1:
optional: true
path-parse@1.0.7: {}
pbf@4.0.1: pbf@4.0.1:
dependencies: dependencies:
resolve-protobuf-schema: 2.1.0 resolve-protobuf-schema: 2.1.0
picocolors@1.1.1: {} picocolors@1.1.1: {}
picomatch@4.0.3:
optional: true
postcss@8.5.8: postcss@8.5.8:
dependencies: dependencies:
nanoid: 3.3.11 nanoid: 3.3.11
@ -690,24 +1121,147 @@ snapshots:
quickselect@3.0.0: {} quickselect@3.0.0: {}
readdirp@4.1.2: {}
resolve-protobuf-schema@2.1.0: resolve-protobuf-schema@2.1.0:
dependencies: dependencies:
protocol-buffers-schema: 3.6.0 protocol-buffers-schema: 3.6.0
resolve@1.22.11:
dependencies:
is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
rw@1.3.3: {} rw@1.3.3: {}
rxjs@7.8.2:
dependencies:
tslib: 2.8.1
sass-embedded-all-unknown@1.98.0:
dependencies:
sass: 1.98.0
optional: true
sass-embedded-android-arm64@1.98.0:
optional: true
sass-embedded-android-arm@1.98.0:
optional: true
sass-embedded-android-riscv64@1.98.0:
optional: true
sass-embedded-android-x64@1.98.0:
optional: true
sass-embedded-darwin-arm64@1.98.0:
optional: true
sass-embedded-darwin-x64@1.98.0:
optional: true
sass-embedded-linux-arm64@1.98.0:
optional: true
sass-embedded-linux-arm@1.98.0:
optional: true
sass-embedded-linux-musl-arm64@1.98.0:
optional: true
sass-embedded-linux-musl-arm@1.98.0:
optional: true
sass-embedded-linux-musl-riscv64@1.98.0:
optional: true
sass-embedded-linux-musl-x64@1.98.0:
optional: true
sass-embedded-linux-riscv64@1.98.0:
optional: true
sass-embedded-linux-x64@1.98.0:
optional: true
sass-embedded-unknown-all@1.98.0:
dependencies:
sass: 1.98.0
optional: true
sass-embedded-win32-arm64@1.98.0:
optional: true
sass-embedded-win32-x64@1.98.0:
optional: true
sass-embedded@1.98.0:
dependencies:
'@bufbuild/protobuf': 2.11.0
colorjs.io: 0.5.2
immutable: 5.1.5
rxjs: 7.8.2
supports-color: 8.1.1
sync-child-process: 1.0.2
varint: 6.0.0
optionalDependencies:
sass-embedded-all-unknown: 1.98.0
sass-embedded-android-arm: 1.98.0
sass-embedded-android-arm64: 1.98.0
sass-embedded-android-riscv64: 1.98.0
sass-embedded-android-x64: 1.98.0
sass-embedded-darwin-arm64: 1.98.0
sass-embedded-darwin-x64: 1.98.0
sass-embedded-linux-arm: 1.98.0
sass-embedded-linux-arm64: 1.98.0
sass-embedded-linux-musl-arm: 1.98.0
sass-embedded-linux-musl-arm64: 1.98.0
sass-embedded-linux-musl-riscv64: 1.98.0
sass-embedded-linux-musl-x64: 1.98.0
sass-embedded-linux-riscv64: 1.98.0
sass-embedded-linux-x64: 1.98.0
sass-embedded-unknown-all: 1.98.0
sass-embedded-win32-arm64: 1.98.0
sass-embedded-win32-x64: 1.98.0
sass@1.98.0:
dependencies:
chokidar: 4.0.3
immutable: 5.1.5
source-map-js: 1.2.1
optionalDependencies:
'@parcel/watcher': 2.5.6
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
supercluster@8.0.1: supercluster@8.0.1:
dependencies: dependencies:
kdbush: 4.0.2 kdbush: 4.0.2
supports-color@8.1.1:
dependencies:
has-flag: 4.0.0
supports-preserve-symlinks-flag@1.0.0: {}
sync-child-process@1.0.2:
dependencies:
sync-message-port: 1.2.0
sync-message-port@1.2.0: {}
tinyqueue@3.0.0: {} tinyqueue@3.0.0: {}
tslib@2.8.1: {}
typescript@4.9.5: {} typescript@4.9.5: {}
typescript@5.9.3: {} typescript@5.9.3: {}
varint@6.0.0: {}
vue@3.5.30(typescript@5.9.3): vue@3.5.30(typescript@5.9.3):
dependencies: dependencies:
'@vue/compiler-dom': 3.5.30 '@vue/compiler-dom': 3.5.30

View file

@ -1,2 +1,3 @@
onlyBuiltDependencies: onlyBuiltDependencies:
- '@parcel/watcher'
- esbuild - esbuild

View file

@ -2,5 +2,5 @@ package static
// gen.go - checked into git // gen.go - checked into git
// This file is overwritten during Nix builds with hashed paths // This file is overwritten during Nix builds with hashed paths
const BundlePathCSS = "/static/gen/css/style.css" const BundlePathCSS = "/static/gen/main.css"
const BundlePathJS = "/static/gen/js/bundle.js" const BundlePathJS = "/static/gen/main.js"

12
ts/global.d.ts vendored Normal file
View file

@ -0,0 +1,12 @@
import * as bootstrap from 'bootstrap';
declare global {
interface Window {
Alpine: any;
SSEManager: any;
createAppPlanning: any;
bootstrap: typeof bootstrap;
}
}
export {};

View file

@ -1,5 +1,55 @@
import Alpine from './vendor/alpinejs-3.15.8.js';
import { createApp } from 'vue'; import { createApp } from 'vue';
import App from './app.vue'; import App from './app.vue';
import { SSEManager } from './sse-manager';
import { SetupSidebar } from "./sidebar";
import 'maplibre-gl/dist/maplibre-gl.css'; import 'maplibre-gl/dist/maplibre-gl.css';
// Import Bootstrap SCSS
import './style/style.scss';
// Import Bootstrap JavaScript and make it available globally
import * as bootstrap from 'bootstrap';
window.bootstrap = bootstrap;
import { Planning } from './app/planning';
// Make Alpine available on window for inline Alpine
window.Alpine = Alpine;
// Make SSEManager available to all the JavaScript
window.SSEManager = SSEManager;
function createAppPlanning() {
const app = createApp({
data() {
return {
count: 0
}
}
});
}
window.createAppPlanning = createAppPlanning;
// Wait for DOM to be ready, then initialize Alpine
document.addEventListener("DOMContentLoaded", () => {
Alpine.start();
SSEManager.connect("/api/events");
SetupSidebar();
});
interface GreetingComponent {
message: string;
name: string;
updateMessage(): void;
}
Alpine.data('greeting', (): GreetingComponent => ({
message: 'Welcome to Alpine + TypeScript!',
name: 'World',
updateMessage() {
this.message = 'Message updated at ' + new Date().toLocaleTimeString();
}
}));
createApp(App).mount('#app'); createApp(App).mount('#app');

75
ts/sidebar.ts Normal file
View file

@ -0,0 +1,75 @@
export function SetupSidebar() {
console.log("setting up sidebar");
var popoverTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="popover"]'),
);
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl);
});
console.log("Initialized ", popoverTriggerList.length, " popovers");
var tooltipTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="tooltip"]'),
);
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
let t = new bootstrap.Tooltip(tooltipTriggerEl);
return t;
});
console.log("Initialized ", tooltipTriggerList.length, " tooltips");
restoreLocalStorage();
setTooltipsForSidebar();
SSEManager.subscribe("*", function (e) {
if (e.type != "heartbeat") {
updateUserState();
}
});
document.addEventListener("alpine:init", () => {
Alpine.store("user", USER);
})
document.getElementById("sidebarToggle").addEventListener("click", () => {
const sidebar = document.getElementById("sidebar");
sidebar.classList.toggle("collapsed");
document.getElementById("content").classList.toggle("expanded");
setTooltipsForSidebar();
localStorage.setItem(
"sidebar.expanded",
(!sidebar.classList.contains("collapsed")).toString(),
);
});
}
function restoreLocalStorage() {
const expanded = localStorage.getItem("sidebar.expanded");
if (expanded == "false") {
document.getElementById("sidebar").classList.add("collapsed");
document.getElementById("content").classList.add("expanded");
} else {
document.getElementById("sidebar").classList.remove("collapsed");
document.getElementById("content").classList.remove("expanded");
localStorage.setItem("sidebar.expanded", "true");
}
}
function setTooltipsForSidebar() {
const sidebarTooltips = document.querySelectorAll(
'#sidebar [data-bs-toggle="tooltip"]',
);
const isExpanded = document
.getElementById("content")
.classList.contains("expanded");
sidebarTooltips.forEach((t) => {
const tooltip = bootstrap.Tooltip.getOrCreateInstance(t);
if (isExpanded) {
tooltip.enable();
} else {
tooltip.disable();
}
});
}
async function updateUserState() {
const response = await fetch("/api/user/self");
const data = await response.json();
// Update properties instead of replacing the whole store which leverages Alpine's reactivity
const store_user = Alpine.store("user");
Object.keys(data).forEach(key => {
store_user[key] = data[key];
});
}

140
ts/style/sidebar.scss Normal file
View file

@ -0,0 +1,140 @@
.logo-container {
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s ease;
}
.logo {
max-width: 100%;
height: auto;
transition: all 0.3s ease;
}
#sidebar {
background-color: $off-white;
min-height: 100vh;
transition: all 0.3s;
width: 250px;
position: fixed;
z-index: 1000;
padding: 20px;
}
#sidebar.collapsed {
width: 70px;
padding: 20px 10px;
}
/* Logo style when sidebar is collapsed */
#sidebar.collapsed .logo-container {
width: 100%;
}
#sidebar.collapsed .logo-img {
max-width: 40px; /* smaller size for collapsed state */
}
#content {
transition: all 0.3s;
margin-left: 250px;
padding: 10px;
width: calc(100% - 250px);
}
#content.expanded {
margin-left: 70px;
width: calc(100% - 70px);
}
.sidebar-header {
padding-bottom: 20px;
border-bottom: 1px solid $off-black;
margin-bottom: 20px;
overflow: hidden;
white-space: nowrap;
display: flex;
justify-content: center; /* Center for the logo */
}
.sidebar-menu {
list-style: none;
padding: 0;
}
.sidebar-menu li {
padding: 10px 0;
}
.sidebar-menu li a {
text-decoration: none;
color: $off-black;
display: flex;
align-items: center;
overflow: hidden;
white-space: nowrap;
}
.sidebar-menu li a:hover {
color: $primary;
}
.sidebar-menu .menu-icon {
font-size: 1.2rem;
min-width: 30px;
display: flex;
justify-content: center;
}
.sidebar-menu .menu-icon svg {
width: 1.5em;
height: 1.5em;
}
.sidebar-menu .menu-text {
transition: opacity 0.3s;
}
#sidebar.collapsed .menu-text {
opacity: 0;
visibility: hidden;
width: 0;
}
#sidebar.collapsed .sidebar-header h4 {
opacity: 0;
visibility: hidden;
}
#sidebar.collapsed .sidebar-menu .menu-icon {
min-width: 100%;
font-size: 1.5rem;
}
#sidebarToggle {
position: absolute;
left: calc(250px - 15px);
top: 50%;
transform: translateY(-50%);
z-index: 1050;
width: 30px;
height: 30px;
border-radius: 50%;
border: 1px solid #dee2e6;
display: flex;
align-items: center;
transition: left 0.3s;
padding: 0;
}
#sidebarToggle i {
transition: transform 0.3s;
}
#sidebar.collapsed > #sidebarToggle {
left: calc(70px - 15px);
}
#sidebar > #sidebarToggle i {
position: relative;
left: 5px;
}
#sidebar.collapsed > #sidebarToggle i {
transform: rotate(180deg);
}

63
ts/style/style.scss Normal file
View file

@ -0,0 +1,63 @@
@use "sass:map";
// 1. Include specific theme variables
$primary: #F76436;
$secondary: #3C552D;
$success: #8BAE67;
$warning: #FFC01B;
$danger: #6b2737;
$info: #D7B26D;
$dark: #3b1002;
$light: #fde1d8;
$off-white: #F8F9FA;
$off-black: #495057;
$primary-light-4: #FAA489;
// 2. Configure color contrast
$color-contrast-dark: #000;
$color-contrast-light: #fff;
$min-contrast-ratio: 2.0;
$custom-colors: (
"color1": $primary,
"color2": $secondary,
"color3": $success,
"color4": $danger,
"color5": $warning,
"color6": $info,
);
$theme-colors: map.merge(
(
"primary": $primary,
"secondary": $secondary,
"success": $success,
"danger": $danger,
"warning": $warning,
"info": $info,
"dark": $dark,
"light": $light
),
$custom-colors
);
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
// Make custom SVG icons about the same size as other icons
i.bi svg {
height: 18px;
width: 18px;
}
@import "bootstrap/scss/functions";
// Import Bootstrap's variables (this merges with your custom variables)
@import "bootstrap/scss/variables";
// Import Bootstrap's mixins
@import "bootstrap/scss/mixins";
// Import all of Bootstrap (or pick specific components)
@import "bootstrap/scss/bootstrap";
@import "./sidebar.scss";