diff --git a/app/components/Form.tsx b/app/components/Form.tsx new file mode 100644 index 0000000..ed8fe32 --- /dev/null +++ b/app/components/Form.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { + Accordion, + Button, + Checkbox, + Label, + TextInput, + ToggleSwitch, +} from "flowbite-react"; +import { useContext, useState } from "react"; + +import { + MapSceneContext, + MapSceneContextType, +} from "../providers/map-scene-provider"; +import { Mesh, MeshStandardMaterial } from "three"; + +export function Form() { + const [enabled, setEnabled] = useState(true); + const { mapScene } = useContext(MapSceneContext) as MapSceneContextType; + + function handleChange() { + if (!mapScene) return; + + mapScene.toggleClippingBox(); + setEnabled(!enabled); + } + + function handleCheckboxChange(name: string) { + if (!mapScene) return; + + const mesh = mapScene.model.getObjectByName(name); + if (mesh) { + mesh.visible = !mesh.visible; + } + } + + return ( +
+
+ + + + Layers + +
+ {mapScene?.model.children.map((child) => { + const key = `toggle-visibility-${child.name}`; + const color = `#${( + (child as Mesh).material as MeshStandardMaterial + ).color.getHexString()}`; + return ( +
+ + handleCheckboxChange(child.name)} + className="ml-2" + /> + +
+ ); + })} +
+
+
+
+
+
+ ); +} diff --git a/app/components/Map.tsx b/app/components/Map.tsx index bb3359e..17f35fd 100644 --- a/app/components/Map.tsx +++ b/app/components/Map.tsx @@ -1,17 +1,31 @@ "use client"; -import { useEffect, useRef } from "react"; -import { init } from "../three/utils/init"; +import { useContext, useEffect, useRef } from "react"; +import { MapScene } from "../three/MapScene"; +import { + MapSceneContext, + MapSceneContextType, +} from "../providers/map-scene-provider"; export function Map() { const divRef = useRef(null); + const { setMapScene } = useContext(MapSceneContext) as MapSceneContextType; useEffect(() => { let ignore = false; if (!divRef.current) return; + async function loadScene() { + if (divRef.current) { + const _mapScene = await MapScene.create(divRef.current, "20"); + if (_mapScene) { + setMapScene(_mapScene); + } + } + } + if (!ignore) { - init(divRef.current); + loadScene(); } return () => { diff --git a/app/page.tsx b/app/page.tsx index a989dea..6359843 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,9 +1,21 @@ import { Map } from "./components/Map"; +import { Form } from "./components/Form"; +import { MapSceneProvider } from "./providers/map-scene-provider"; + export default function Home() { return (
- + +
+
+ +
+
+
+
+
+
); diff --git a/app/providers/map-scene-provider.tsx b/app/providers/map-scene-provider.tsx new file mode 100644 index 0000000..1e62657 --- /dev/null +++ b/app/providers/map-scene-provider.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { + createContext, + Dispatch, + ReactNode, + SetStateAction, + useState, +} from "react"; + +import { MapScene } from "../three/MapScene"; + +// Declare MapScene context +export type MapSceneContextType = { + mapScene: MapScene | null; + setMapScene: Dispatch>; +}; + +// Context for MapScene +export const MapSceneContext = createContext(null); + +// Context provider for MapScene +export const MapSceneProvider = ({ children }: { children: ReactNode }) => { + const [mapScene, setMapScene] = useState(null); + + return ( + + {children} + + ); +}; diff --git a/app/three/MapScene.ts b/app/three/MapScene.ts new file mode 100644 index 0000000..4386dfe --- /dev/null +++ b/app/three/MapScene.ts @@ -0,0 +1,105 @@ +import { AxesHelper, Group, Scene } from "three"; +import { buildMeshes } from "./utils/build-meshes"; +import { Extent, buildScene } from "./utils/build-scene"; +import { getMetadata } from "./utils/utils"; +import { MODEL_ID, SERVICE_URL } from "./config"; +import { buildClippingplanes } from "./utils/build-clipping-planes"; +import { buildGrid } from "./utils/build-grid"; +import { DragControls } from "three/examples/jsm/Addons.js"; + +export class MapScene { + private _scene: Scene; + private _dragControls: DragControls; + private _model: Group; + + constructor(scene: Scene, model: Group, dragControls: DragControls) { + this._scene = scene; + this._dragControls = dragControls; + this._model = model; + } + + static async create(container: HTMLElement, modelId: string) { + const { scene, model, dragControls } = await init(container, modelId); + return new MapScene(scene, model, dragControls); + } + + get scene() { + return this._scene; + } + + get model() { + return this._model; + } + + toggleClippingBox() { + const box = this._scene?.getObjectByName("clipping-box"); + if (box) { + // Set DragControls + if (box.visible) { + this._dragControls.enabled = false; + } else { + this._dragControls.enabled = true; + } + + box.visible = !box.visible; + } + } + + toggleLayerVisibility(layerName: string) { + const mesh = this._model.getObjectByName(layerName); + if (mesh) { + mesh.visible = !mesh.visible; + } + } +} + +async function init(container: HTMLElement, modelId = MODEL_ID) { + const modelData = await getMetadata(SERVICE_URL + modelId); + const mappedFeatures = modelData.mappedfeatures; + const modelarea = modelData.modelarea; + + const extent: Extent = { + xmin: modelarea.x.min, + xmax: modelarea.x.max, + ymin: modelarea.y.min, + ymax: modelarea.y.max, + zmin: modelarea.z.min, + zmax: modelarea.z.max, + }; + + const { renderer, scene, camera, controls } = buildScene(container, extent); + + // Build the 3D model + const meshes = await buildMeshes(mappedFeatures); + const model = new Group(); + model.add(...meshes); + model.name = "geologic-model"; + scene.add(model); + + // Build the clipping planes and add them to the scene + const { planes, dragControls } = buildClippingplanes( + renderer, + camera, + controls, + extent, + meshes, + scene + ); + + // Add clipping planes to the meshes + for (let mesh of meshes) { + mesh.material.clippingPlanes = planes; + } + + // Add a coordinate grid to the scene + const { gridHelper, annotations } = buildGrid(extent); + const annotationsGroup = new Group(); + annotationsGroup.name = "coordinate-grid"; + annotationsGroup.add(...annotations); + scene.add(gridHelper, annotationsGroup); + + //const axesHelper = new AxesHelper(5); + //scene.add(axesHelper); + + return { scene, model, dragControls }; +} diff --git a/app/three/utils/build-clipping-plane.ts b/app/three/utils/build-clipping-planes.ts similarity index 66% rename from app/three/utils/build-clipping-plane.ts rename to app/three/utils/build-clipping-planes.ts index 833f5b4..3775212 100644 --- a/app/three/utils/build-clipping-plane.ts +++ b/app/three/utils/build-clipping-planes.ts @@ -2,7 +2,10 @@ import { BufferAttribute, BufferGeometry, DoubleSide, + EdgesGeometry, Group, + LineBasicMaterial, + LineSegments, Mesh, MeshBasicMaterial, MeshStandardMaterial, @@ -15,11 +18,7 @@ import { Vector3, WebGLRenderer, } from "three"; -import { - ConvexGeometry, - DragControls, - OrbitControls, -} from "three/examples/jsm/Addons.js"; +import { DragControls, OrbitControls } from "three/examples/jsm/Addons.js"; import { Extent } from "./build-scene"; import earcut from "earcut"; @@ -30,11 +29,19 @@ enum Orientation { } type PlaneMesh = Mesh; +type EdgeMesh = LineSegments< + EdgesGeometry, + LineBasicMaterial, + Object3DEventMap +>; type PlaneMeshMap = { [key in Orientation]: PlaneMesh; }; +type EdgeMashMap = { + [key in Orientation]: EdgeMesh; +}; -export function createClippingPlanes( +export function buildClippingplanes( renderer: WebGLRenderer, camera: PerspectiveCamera, orbitControls: OrbitControls, @@ -60,13 +67,13 @@ export function createClippingPlanes( }, ]; - const planeMeshes: Mesh< - PlaneGeometry, - MeshBasicMaterial, - Object3DEventMap - >[] = []; + const planeMeshes: PlaneMesh[] = []; + const edgeMeshes: EdgeMesh[] = []; const planes: Plane[] = []; - let planeMeshMap = {} as Partial; + const planeMeshMap = {} as Partial; + const edgeMeshMap = {} as Partial; + + // Create plane meshes for (let p of planesData) { let name; let planeCenter; @@ -79,7 +86,7 @@ export function createClippingPlanes( planeCenter = new Vector3( -p.d, extent.ymax - width / 2, - extent.zmax - height / 2 + extent.zmin + height / 2 ); } else if (p.orientation === Orientation.Y) { name = Orientation.Y; @@ -88,7 +95,7 @@ export function createClippingPlanes( planeCenter = new Vector3( extent.xmax - width / 2, -p.d, - extent.zmax - height / 2 + extent.zmin + height / 2 ); } else { name = Orientation.Z; @@ -101,46 +108,65 @@ export function createClippingPlanes( ); } - // Visual representation of the clipping plane - // Plane is given in Hesse normal form + // Plane is given in Hesse normal form: a * x + b* y + c * y + d = 0, where normal = (a, b, c) and d = d const plane = new Plane(p.normal, p.d); - // Dragging Mechanism + // Visual representation of the clipping plane + const planeGeometry = new PlaneGeometry(width, height); const planeMesh = new Mesh( - new PlaneGeometry(width, height), + planeGeometry, new MeshBasicMaterial({ visible: true, - color: 0xff0000, + color: 0xa92a4e, transparent: true, opacity: 0.1, side: DoubleSide, - clipIntersection: false, }) ); planeMesh.name = name; planeMesh.userData.plane = plane; + // Create the edges geometry + const edgesGeometry = new EdgesGeometry(planeGeometry); + const edgesMaterial = new LineBasicMaterial({ color: 0xa92a4e }); + const edges = new LineSegments(edgesGeometry, edgesMaterial); + + // Translate meshes planeMesh.position.set(planeCenter.x, planeCenter.y, planeCenter.z); + edges.position.set(planeCenter.x, planeCenter.y, planeCenter.z); + + // Rotate meshes if (p.orientation === Orientation.X) { planeMesh.rotateY(Math.PI / 2); planeMesh.rotateZ(Math.PI / 2); + edges.rotateY(Math.PI / 2); + edges.rotateZ(Math.PI / 2); } else if (p.orientation === Orientation.Y) { planeMesh.rotateX(Math.PI / 2); + edges.rotateX(Math.PI / 2); } + planeMeshes.push(planeMesh); + edgeMeshes.push(edges); planes.push(plane); planeMeshMap[p.orientation] = planeMesh; + edgeMeshMap[p.orientation] = edges; } - for (let pm of planeMeshes) { - // Let clipping planes clip each other - const clippingPlanes = planes.filter( - (p) => !p.normal.equals(pm.userData.plane.normal) - ); + // Add meshes to the scene + const planeMeshGroup = new Group(); + planeMeshGroup.name = "clipping-planes"; + planeMeshGroup.add(...planeMeshes); - pm.material.clippingPlanes = clippingPlanes; - } + const edgeMeshGroup = new Group(); + edgeMeshGroup.name = "clipping-plane-edges"; + edgeMeshGroup.add(...edgeMeshes); + + const clippingBox = new Group(); + clippingBox.add(planeMeshGroup, edgeMeshGroup); + clippingBox.name = "clipping-box"; + scene.add(clippingBox); // Enable DragControls for the clipping planes const dragControls = new DragControls( @@ -149,15 +175,17 @@ export function createClippingPlanes( renderer.domElement ); - dragControls.addEventListener("dragstart", () => { + dragControls.addEventListener("dragstart", (event) => { + const object = event.object as PlaneMesh; // Disable OrbitControls when dragging starts orbitControls.enabled = false; // Remove existing cap meshes - let capMeshGroup = scene.getObjectByName("cap-mesh-group"); + const capMeshGroupName = `cap-mesh-group-${object.name}`; + let capMeshGroup = scene.getObjectByName(capMeshGroupName); while (capMeshGroup) { scene.remove(capMeshGroup); - capMeshGroup = scene.getObjectByName("cap-mesh-group"); + capMeshGroup = scene.getObjectByName(capMeshGroupName); } }); @@ -171,7 +199,9 @@ export function createClippingPlanes( const plane = event.object.userData.plane; const width = object.geometry.parameters.width; const height = object.geometry.parameters.height; + let orientation: Orientation; if (object.name === Orientation.Z) { + orientation = Orientation.Z; // Fix rotation of dragged mesh event.object.rotation.set(0, 0, 0); @@ -187,14 +217,28 @@ export function createClippingPlanes( // Reset position of plane plane.constant = newZ; - // Set position of dragged mesh + // Set position of dragged meshes object.position.x = extent.xmax - width / 2; object.position.y = extent.ymax - height / 2; object.position.z = newZ; - // Resize other meshes - resizeMeshes(Orientation.Z, newZ, planeMeshMap as PlaneMeshMap, extent); + const edgeMesh = edgeMeshMap[Orientation.Z]; + if (edgeMesh) { + edgeMesh.position.x = extent.xmax - width / 2; + edgeMesh.position.y = extent.ymax - height / 2; + edgeMesh.position.z = newZ; + } + + // Resize other meshes to disable dragging of clipped surface parts + resizeMeshes( + Orientation.Z, + newZ, + planeMeshMap as PlaneMeshMap, + edgeMeshMap as EdgeMashMap, + extent + ); } else if (object.name === Orientation.Y) { + orientation = Orientation.Y; // Fix rotation of dragged mesh event.object.rotation.set(Math.PI / 2, 0, 0); @@ -213,11 +257,26 @@ export function createClippingPlanes( // Set position of dragged mesh object.position.x = extent.xmax - width / 2; object.position.y = newY; - object.position.z = extent.zmax - height / 2; + object.position.z = extent.zmin + height / 2; + + const edgeMesh = edgeMeshMap[Orientation.Y]; + if (edgeMesh) { + edgeMesh.position.x = extent.xmax - width / 2; + edgeMesh.position.y = newY; + edgeMesh.position.z = extent.zmin + height / 2; + } // Resize other meshes - resizeMeshes(Orientation.Y, newY, planeMeshMap as PlaneMeshMap, extent); + resizeMeshes( + Orientation.Y, + newY, + planeMeshMap as PlaneMeshMap, + edgeMeshMap as EdgeMashMap, + extent + ); } else { + orientation = Orientation.X; + // Fix rotation of dragged mesh event.object.rotation.set(0, Math.PI / 2, Math.PI / 2); @@ -236,124 +295,178 @@ export function createClippingPlanes( // Set position of dragged mesh object.position.x = newX; object.position.y = extent.ymax - width / 2; - object.position.z = extent.zmax - height / 2; + object.position.z = extent.zmin + height / 2; + + const edgeMesh = edgeMeshMap[Orientation.X]; + if (edgeMesh) { + edgeMesh.position.x = newX; + edgeMesh.position.y = extent.ymax - width / 2; + edgeMesh.position.z = extent.zmin + height / 2; + } // Resize other meshes - resizeMeshes(Orientation.X, newX, planeMeshMap as PlaneMeshMap, extent); + resizeMeshes( + Orientation.X, + newX, + planeMeshMap as PlaneMeshMap, + edgeMeshMap as EdgeMashMap, + extent + ); } // Remove existing cap meshes - let capMeshGroup = scene.getObjectByName("cap-mesh-group"); + const capMeshGroupName = `cap-mesh-group-${object.name}`; + let capMeshGroup = scene.getObjectByName(capMeshGroupName); while (capMeshGroup) { scene.remove(capMeshGroup); - capMeshGroup = scene.getObjectByName("cap-mesh-group"); + capMeshGroup = scene.getObjectByName(capMeshGroupName); } - const capMeshes = generateCapMeshes(meshes, plane); + // Generate new cap meshes + const capMeshes = generateCapMeshes(meshes, plane, planes, orientation); + // Add new cap meshes if (capMeshes.length > 0) { - // Add new cap meshes const newCapMeshGroup = new Group(); newCapMeshGroup.add(...capMeshes); - newCapMeshGroup.name = "cap-mesh-group"; + newCapMeshGroup.name = capMeshGroupName; scene.add(newCapMeshGroup); } }); - return { planeMeshes, planes }; + return { planes, dragControls }; } function resizeMeshes( orientation: Orientation, newCoordinate: number, planeMeshes: PlaneMeshMap, + edgeMeshes: EdgeMashMap, extent: Extent ) { if (orientation === Orientation.X) { // Resize y-clipping plane let planeMesh = planeMeshes[Orientation.Y]; + let edgeMesh = edgeMeshes[Orientation.Y]; let width = extent.xmax - newCoordinate; let height = planeMesh.geometry.parameters.height; + let planeGeometry = new PlaneGeometry(width, height); const y = planeMesh.position.y; planeMesh.geometry.dispose(); - planeMesh.geometry = new PlaneGeometry(width, height); + planeMesh.geometry = planeGeometry; planeMesh.position.set( extent.xmax - width / 2, y, - extent.zmax - height / 2 + extent.zmin + height / 2 ); + edgeMesh.geometry.dispose(); + edgeMesh.geometry = new EdgesGeometry(planeGeometry); + edgeMesh.position.set(extent.xmax - width / 2, y, extent.zmin + height / 2); // Resize z-clipping-plane planeMesh = planeMeshes[Orientation.Z]; + edgeMesh = edgeMeshes[Orientation.Z]; width = extent.xmax - newCoordinate; height = planeMesh.geometry.parameters.height; + planeGeometry = new PlaneGeometry(width, height); const z = planeMesh.position.z; planeMesh.geometry.dispose(); - planeMesh.geometry = new PlaneGeometry(width, height); + planeMesh.geometry = planeGeometry; planeMesh.position.set( extent.xmax - width / 2, extent.ymax - height / 2, z ); + edgeMesh.geometry.dispose(); + edgeMesh.geometry = new EdgesGeometry(planeGeometry); + edgeMesh.position.set(extent.xmax - width / 2, extent.ymax - height / 2, z); } else if (orientation === Orientation.Y) { // Resize x-clipping plane let planeMesh = planeMeshes[Orientation.X]; + let edgeMesh = edgeMeshes[Orientation.X]; let width = extent.ymax - newCoordinate; let height = planeMesh.geometry.parameters.height; + let planeGeometry = new PlaneGeometry(width, height); const x = planeMesh.position.x; planeMesh.geometry.dispose(); - planeMesh.geometry = new PlaneGeometry(width, height); + planeMesh.geometry = planeGeometry; planeMesh.position.set( x, extent.ymax - width / 2, - extent.zmax - height / 2 + extent.zmin + height / 2 ); + edgeMesh.geometry.dispose(); + edgeMesh.geometry = new EdgesGeometry(planeGeometry); + edgeMesh.position.set(x, extent.ymax - width / 2, extent.zmin + height / 2); // Resize z-clipping-plane planeMesh = planeMeshes[Orientation.Z]; + edgeMesh = edgeMeshes[Orientation.Z]; width = planeMesh.geometry.parameters.width; height = extent.ymax - newCoordinate; + planeGeometry = new PlaneGeometry(width, height); const z = planeMesh.position.z; planeMesh.geometry.dispose(); - planeMesh.geometry = new PlaneGeometry(width, height); + planeMesh.geometry = planeGeometry; planeMesh.position.set( extent.xmax - width / 2, extent.ymax - height / 2, z ); + edgeMesh.geometry.dispose(); + edgeMesh.geometry = new EdgesGeometry(planeGeometry); + edgeMesh.position.set(extent.xmax - width / 2, extent.ymax - height / 2, z); } else if (orientation === Orientation.Z) { // Resize x-clipping-plane let planeMesh = planeMeshes[Orientation.X]; + let edgeMesh = edgeMeshes[Orientation.X]; let width = planeMesh.geometry.parameters.width; let height = newCoordinate - extent.zmin; + let planeGeometry = new PlaneGeometry(width, height); const x = planeMesh.position.x; planeMesh.geometry.dispose(); - planeMesh.geometry = new PlaneGeometry(width, height); + planeMesh.geometry = planeGeometry; planeMesh.position.set( x, extent.ymax - width / 2, - extent.zmax - height / 2 + extent.zmin + height / 2 ); + edgeMesh.geometry.dispose(); + edgeMesh.geometry = new EdgesGeometry(planeGeometry); + edgeMesh.position.set(x, extent.ymax - width / 2, extent.zmin + height / 2); // Resize y-clipping plane planeMesh = planeMeshes[Orientation.Y]; + edgeMesh = edgeMeshes[Orientation.Y]; width = planeMesh.geometry.parameters.width; height = newCoordinate - extent.zmin; + planeGeometry = new PlaneGeometry(width, height); const y = planeMesh.position.y; planeMesh.geometry.dispose(); - planeMesh.geometry = new PlaneGeometry(width, height); + planeMesh.geometry = planeGeometry; planeMesh.position.set( extent.xmax - width / 2, y, - extent.zmax - height / 2 + extent.zmin + height / 2 ); + + edgeMesh.geometry.dispose(); + edgeMesh.geometry = new EdgesGeometry(planeGeometry); + edgeMesh.position.set(extent.xmax - width / 2, y, extent.zmin + height / 2); } } // Extract contour and generate cap -function generateCapMeshes(meshes: Mesh[], plane: Plane) { +function generateCapMeshes( + meshes: Mesh[], + plane: Plane, + planes: Plane[], + orientation: Orientation +) { const capMeshes: Mesh[] = []; + + // Iterate over the list of geologic meshes for (let mesh of meshes) { const position = mesh.geometry.attributes.position.array; const indices = mesh.geometry.index ? mesh.geometry.index.array : null; @@ -389,14 +502,20 @@ function generateCapMeshes(meshes: Mesh[], plane: Plane) { } } + // Intersection surface can be a multipolygon consisting of disconnected polygons const polygons: Vector3[][] = buildPolygons(edges); + // Clip cap surfaces with clipping planes + const clippingPlanes = planes.filter((p) => !p.normal.equals(plane.normal)); + + const offset = orientation === Orientation.Z ? 1 : -1; const material = new MeshStandardMaterial({ color: (mesh.material as MeshStandardMaterial).color, side: DoubleSide, polygonOffset: true, - polygonOffsetFactor: -1, - polygonOffsetUnits: -1, + polygonOffsetFactor: offset, + polygonOffsetUnits: offset, + clippingPlanes, }); const localMeshes = polygons.map((polygon) => { @@ -405,7 +524,6 @@ function generateCapMeshes(meshes: Mesh[], plane: Plane) { const capMesh = new Mesh(geometry, material); // Offset mesh to avoid flickering - const offset = -1; const normal = plane.normal.clone().multiplyScalar(offset); const positionAttr = capMesh.geometry.attributes.position; @@ -518,13 +636,6 @@ function triangulatePolygon(vertices: Vector3[], plane: Plane) { return geometry; } -// Compute the centroid of a list of 2D vertices -function computeCentroid(vertices: Vector2[]): Vector2 { - const centroid = new Vector2(); - vertices.forEach((v) => centroid.add(v)); - return centroid.divideScalar(vertices.length); -} - // Function to find the intersection point between an edge and a plane function intersectEdge(v1: Vector3, v2: Vector3, d1: number, d2: number) { const t = d1 / (d1 - d2); diff --git a/app/three/utils/build-meshes.ts b/app/three/utils/build-meshes.ts index a44a1ee..6ef2144 100644 --- a/app/three/utils/build-meshes.ts +++ b/app/three/utils/build-meshes.ts @@ -4,12 +4,8 @@ import { DoubleSide, Mesh, MeshStandardMaterial, - Plane, } from "three"; -import { uniforms } from "./uniforms"; -import { shader } from "./shader"; - import { fetchTriangleIndices } from "./fetch-triangle-indices"; import { fetchVertices } from "./fetch-vertices"; import { TRIANGLE_INDICES_URL, VERTICES_URL } from "../config"; @@ -21,6 +17,17 @@ interface MappedFeature { preview: { legend_color: string; legend_text: string }; } +export async function buildMeshes(mappedFeatures: MappedFeature[]) { + const meshes = []; + for (let i = 0; i < mappedFeatures.length; i++) { + const layerData = mappedFeatures[i]; + const mesh = await buildMesh(layerData); + meshes.push(mesh); + } + + return meshes; +} + async function buildMesh(layerData: MappedFeature) { const color = `#${layerData.preview.legend_color}`; const name = layerData.preview.legend_text; @@ -43,23 +50,13 @@ async function buildMesh(layerData: MappedFeature) { const material = new MeshStandardMaterial({ color: color, - metalness: 0.1, + metalness: 0.0, roughness: 0.75, flatShading: true, side: DoubleSide, wireframe: false, - clipIntersection: false, }); - // material.onBeforeCompile = (materialShader) => { - // materialShader.uniforms.clippingLow = uniforms.clipping.clippingLow; - // materialShader.uniforms.clippingHigh = uniforms.clipping.clippingHigh; - // materialShader.uniforms.clippingScale = uniforms.clipping.clippingScale; - - // materialShader.vertexShader = shader.vertexMeshStandard; - // materialShader.fragmentShader = shader.fragmentClippingMeshStandard; - // }; - const mesh = new Mesh(geometry, material); mesh.name = name; mesh.userData.layerId = geomId; @@ -68,16 +65,3 @@ async function buildMesh(layerData: MappedFeature) { return mesh; } - -export async function buildMeshes(mappedFeatures: MappedFeature[]) { - const meshes = []; - for (let i = 0; i < mappedFeatures.length; i++) { - const layerData = mappedFeatures[i]; - if (layerData.name !== "Topography") { - const mesh = await buildMesh(layerData); - meshes.push(mesh); - } - } - - return meshes; -} diff --git a/app/three/utils/build-scene.ts b/app/three/utils/build-scene.ts index 3b49a91..eb86b74 100644 --- a/app/three/utils/build-scene.ts +++ b/app/three/utils/build-scene.ts @@ -17,7 +17,7 @@ let controls: OrbitControls; let renderer: WebGLRenderer; let camera: PerspectiveCamera; let scene: Scene; -export async function buildScene(container: HTMLElement, extent: Extent) { +export function buildScene(container: HTMLElement, extent: Extent) { const maxSize = getMaxSize(extent); const center = getCenter3D(extent); @@ -31,7 +31,7 @@ export async function buildScene(container: HTMLElement, extent: Extent) { maxSize * 25 ); - camera.position.set(center.x, center.y, extent.zmax + 150000); + camera.position.set(center.x, center.y - 125000, extent.zmax + 100000); camera.lookAt(center); renderer = new WebGLRenderer({ diff --git a/app/three/utils/get-layer-id.ts b/app/three/utils/get-layer-id.ts deleted file mode 100644 index c28b9f6..0000000 --- a/app/three/utils/get-layer-id.ts +++ /dev/null @@ -1,5 +0,0 @@ -let lastId = 0; - -export function stamp(obj: any) { - return obj._id ?? (obj._id = ++lastId); -} diff --git a/app/three/utils/get-metadata.ts b/app/three/utils/get-metadata.ts deleted file mode 100644 index e7314aa..0000000 --- a/app/three/utils/get-metadata.ts +++ /dev/null @@ -1,15 +0,0 @@ -export async function getMetadata(serviceUrl: string) { - const response = await fetch(serviceUrl, { - method: "GET", - mode: "cors", - headers: { - "Content-Type": "application/json", - }, - }); - - if (response.ok) { - return response.json(); - } else { - throw new Error("HTTP error status: " + response.status); - } -} diff --git a/app/three/utils/init.ts b/app/three/utils/init.ts deleted file mode 100644 index 97265bb..0000000 --- a/app/three/utils/init.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { AxesHelper, Group } from "three"; -import { buildMeshes } from "./build-meshes"; -import { Extent, buildScene } from "./build-scene"; -import { getMetadata } from "./get-metadata"; -import { MODEL_ID, SERVICE_URL } from "../config"; -import { createClippingPlanes } from "./build-clipping-plane"; -import { buildGrid } from "./build-grid"; - -export async function init(container: HTMLElement) { - const modelData = await getMetadata(SERVICE_URL + MODEL_ID); - const mappedFeatures = modelData.mappedfeatures; - const modelarea = modelData.modelarea; - - const extent: Extent = { - xmin: modelarea.x.min, - xmax: modelarea.x.max, - ymin: modelarea.y.min, - ymax: modelarea.y.max, - zmin: modelarea.z.min, - zmax: modelarea.z.max, - }; - - const { renderer, scene, camera, controls } = await buildScene( - container, - extent - ); - - // Create the 3D model - const meshes = await buildMeshes(mappedFeatures); - const model = new Group(); - model.add(...meshes); - model.name = "3d-model"; - scene.add(model); - - // Create the clipping planes and add them to the scene - const { planeMeshes, planes } = createClippingPlanes( - renderer, - camera, - controls, - extent, - meshes, - scene - ); - scene.add(...planeMeshes); - - // Add clipping planes to the meshes - for (let mesh of meshes) { - mesh.material.clippingPlanes = planes; - } - - // Add a coordinate grid to the scene - const { gridHelper, annotations } = buildGrid(extent); - const annotationsGroup = new Group(); - annotationsGroup.add(...annotations); - scene.add(gridHelper, annotationsGroup); - - //const axesHelper = new AxesHelper(5); - //scene.add(axesHelper); -} diff --git a/app/three/utils/shader.ts b/app/three/utils/shader.ts deleted file mode 100644 index d813645..0000000 --- a/app/three/utils/shader.ts +++ /dev/null @@ -1,254 +0,0 @@ -export const shader = { - vertex: ` - uniform vec3 color; - varying vec3 pixelNormal; - - void main() { - - pixelNormal = normal; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - - }`, - - vertexClipping: ` - uniform vec3 color; - uniform vec3 clippingLow; - uniform vec3 clippingHigh; - - varying vec3 pixelNormal; - varying vec4 worldPosition; - varying vec3 camPosition; - varying vec3 vNormal; - varying vec3 vPosition; - varying vec2 vUv; - - void main() { - vUv = uv; - vec4 vPos = modelViewMatrix * vec4( position, 1.0 ); - vPosition = vPos.xyz; - - vNormal = normalMatrix * normal; - pixelNormal = normal; - worldPosition = modelMatrix * vec4( position, 1.0 ); - camPosition = cameraPosition; - - // gl_Position = projectionMatrix * vPos; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - - }`, - - fragment: ` - uniform vec3 color; - varying vec3 pixelNormal; - - varying vec3 vNormal; - varying vec3 vPosition; - - // uniform vec3 spotLightPosition; // in world space - // uniform vec3 clight; - // uniform vec3 cspec; - // uniform float roughness; - const float PI = 3.14159; - - - void main( void ) { - - float shade = ( - 3.0 * pow ( abs ( pixelNormal.y ), 2.0 ) - + 2.0 * pow ( abs ( pixelNormal.z ), 2.0 ) - + 1.0 * pow ( abs ( pixelNormal.x ), 2.0 ) - ) / 3.0; - - gl_FragColor = vec4( color * shade, 1.0 ); - //gl_FragColor = vec4(color, 1.0); - - // vec4 lPosition = viewMatrix * vec4( spotLightPosition, 1.0 ); - // vec3 l = normalize(lPosition.xyz - vPosition.xyz); - // vec3 n = normalize( vNormal ); // interpolation destroys normalization, so we have to normalize - // vec3 v = normalize( -vPosition); - // vec3 h = normalize( v + l); - - // // small quantity to prevent divisions by 0 - // float nDotl = max(dot( n, l ),0.000001); - // float lDoth = max(dot( l, h ),0.000001); - // float nDoth = max(dot( n, h ),0.000001); - // float vDoth = max(dot( v, h ),0.000001); - // float nDotv = max(dot( n, v ),0.000001); - - // vec3 specularBRDF = FSchlick(lDoth)*GSmith(nDotv,nDotl)*DGGX(nDoth,roughness*roughness)/(4.0*nDotl*nDotv); - // vec3 outRadiance = (PI* clight * nDotl * specularBRDF); - - // gl_FragColor = vec4(pow( outRadiance, vec3(1.0/2.2)), 1.0); - - }`, - - fragmentClippingFront: ` - uniform sampler2D map; - uniform vec3 color; - uniform vec3 clippingLow; - uniform vec3 clippingHigh; - uniform float clippingScale; - uniform float percent; - - varying vec3 pixelNormal; - varying vec4 worldPosition; - varying vec3 camPosition; - varying vec2 vUv; - - void main( void ) { - - float shade = ( - 3.0 * pow ( abs ( pixelNormal.y ), 2.0 ) - + 2.0 * pow ( abs ( pixelNormal.z ), 2.0 ) - + 1.0 * pow ( abs ( pixelNormal.x ), 2.0 ) - ) / 3.0; - - if ( - worldPosition.x < clippingLow.x - || worldPosition.x > clippingHigh.x - || worldPosition.y < clippingLow.y - || worldPosition.y > clippingHigh.y - || worldPosition.z < (clippingLow.z * clippingScale) - || worldPosition.z > (clippingHigh.z * clippingScale) - ) { - discard; - } else { - gl_FragColor = texture2D(map, vUv); - gl_FragColor.a = percent; - } - - }`, - - vertexMeshStandard: ` - #define STANDARD - - varying vec3 vViewPosition; - varying vec4 worldPosition; - - #include - #include - #include - #include - #include - #include - #include - - void main() { - #include - #include - - #include - #include - - #include - #include - #include - #include - - vViewPosition = -mvPosition.xyz; - - worldPosition = modelMatrix * vec4(position, 1.0); - - #include - #include - } - `, - - fragmentClippingMeshStandard: ` - #define STANDARD - - uniform vec3 diffuse; - uniform vec3 emissive; - uniform float roughness; - uniform float metalness; - uniform float opacity; - - varying vec3 vViewPosition; - varying vec4 worldPosition; - - uniform vec3 clippingLow; - uniform vec3 clippingHigh; - uniform float clippingScale; - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - void main() { - #include - - vec4 diffuseColor = vec4(diffuse, opacity); - ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0)); - vec3 totalEmissiveRadiance = emissive; - - #ifdef TRANSMISSION - float totalTransmission = transmission; - float thicknessFactor = thickness; - #endif - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - vec3 rawDiffuseColor = diffuseColor.rgb; - #include - - // Lighting calculations - #include - #include - - // Ambient occlusion - #include - - vec3 outgoingLight = reflectedLight.directDiffuse - + reflectedLight.indirectDiffuse - + reflectedLight.directSpecular - + reflectedLight.indirectSpecular - + totalEmissiveRadiance; - - // Clipping logic - if (any(greaterThan(worldPosition.xyz, clippingHigh)) || any(lessThan(worldPosition.xyz, clippingLow))) { - discard; - } - - gl_FragColor = vec4(outgoingLight, diffuseColor.a); - } - `, - - invisibleVertexShader: ` - void main() { - vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); - gl_Position = projectionMatrix * mvPosition; - }`, - - invisibleFragmentShader: ` - void main( void ) { - gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - discard; - }`, -}; diff --git a/app/three/utils/uniforms.ts b/app/three/utils/uniforms.ts deleted file mode 100644 index 6d76b2e..0000000 --- a/app/three/utils/uniforms.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Vector3, Color } from "three"; - -export const uniforms = { - clipping: { - // light blue - color: { value: new Color(0x3d9ecb) }, - clippingLow: { value: new Vector3(0, 0, 0) }, - clippingHigh: { value: new Vector3(0, 0, 0) }, - // additional parameter for scaling - clippingScale: { value: 1.0 }, - // topography - map: { value: null }, - percent: { value: 1 }, - }, - caps: { - // red - color: { value: new Color(0xf83610) }, - }, -}; diff --git a/app/three/utils/utils.ts b/app/three/utils/utils.ts index 841f36f..ea1824f 100644 --- a/app/three/utils/utils.ts +++ b/app/three/utils/utils.ts @@ -16,3 +16,19 @@ export function getCenter3D(extent: Extent) { (extent.zmax + extent.zmin) / 2 ); } + +export async function getMetadata(serviceUrl: string) { + const response = await fetch(serviceUrl, { + method: "GET", + mode: "cors", + headers: { + "Content-Type": "application/json", + }, + }); + + if (response.ok) { + return response.json(); + } else { + throw new Error("HTTP error status: " + response.status); + } +} diff --git a/package-lock.json b/package-lock.json index 4b9159a..40bc6a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "earcut": "^3.0.1", + "flowbite-react": "^0.10.2", "next": "15.1.7", "proj4": "^2.15.0", "react": "^19.0.0", @@ -33,7 +34,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "engines": { "node": ">=10" }, @@ -169,6 +169,54 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.6.tgz", + "integrity": "sha512-Vkvsw6EcpMHjvZZdMkSY+djMGFbt7CRssW99Ne8tar2WLnZ/l3dbxeTShbLQj+/s35h+Qb4cmnob+EzwtjrXGQ==", + "dependencies": { + "@floating-ui/utils": "^0.2.6" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.21", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.21.tgz", + "integrity": "sha512-7P5ncDIiYd6RrwpCDbKyFzvabM014QlzlumtDbK3Bck0UueC+Rp8BLS34qcGBcN1pZCTodl4QNnCVmKv4tSxfQ==", + "dependencies": { + "@floating-ui/react-dom": "^2.1.1", + "@floating-ui/utils": "^0.2.6", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -576,7 +624,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -593,7 +640,6 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -607,7 +653,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -616,7 +661,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -624,14 +668,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -775,7 +817,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -788,7 +829,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -797,7 +837,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -819,12 +858,75 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -865,8 +967,7 @@ "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -907,6 +1008,11 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" + }, "node_modules/@types/stats.js": { "version": "0.17.3", "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", @@ -1204,7 +1310,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1216,7 +1321,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1230,14 +1334,12 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1249,8 +1351,7 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/argparse": { "version": "2.0.1", @@ -1467,14 +1568,12 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "engines": { "node": ">=8" }, @@ -1496,7 +1595,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -1575,7 +1673,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, "engines": { "node": ">= 6" } @@ -1619,7 +1716,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -1643,7 +1739,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -1651,6 +1746,11 @@ "node": ">= 6" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -1673,7 +1773,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1684,8 +1783,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-string": { "version": "1.9.1", @@ -1701,7 +1799,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, "engines": { "node": ">= 6" } @@ -1716,7 +1813,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1730,7 +1826,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "bin": { "cssesc": "bin/cssesc" }, @@ -1801,6 +1896,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.0.tgz", + "integrity": "sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -1824,6 +1930,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -1870,14 +1984,12 @@ "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, "node_modules/doctrine": { "version": "2.1.0", @@ -1913,14 +2025,12 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/enhanced-resolve": { "version": "5.18.1", @@ -2507,6 +2617,11 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2566,7 +2681,6 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -2593,7 +2707,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2636,6 +2749,44 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "node_modules/flowbite": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.5.1.tgz", + "integrity": "sha512-7jP1jy9c3QP7y+KU9lc8ueMkTyUdMDvRP+lteSWgY5TigSZjf9K1kqZxmqjhbx2gBnFQxMl1GAjVThCa8cEpKA==", + "dependencies": { + "@popperjs/core": "^2.9.3", + "flowbite-datepicker": "^1.3.0", + "mini-svg-data-uri": "^1.4.3" + } + }, + "node_modules/flowbite-datepicker": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.3.2.tgz", + "integrity": "sha512-6Nfm0MCVX3mpaR7YSCjmEO2GO8CDt6CX8ZpQnGdeu03WUCWtEPQ/uy0PUiNtIJjJZWnX0Cm3H55MOhbD1g+E/g==", + "dependencies": { + "@rollup/plugin-node-resolve": "^15.2.3", + "flowbite": "^2.0.0" + } + }, + "node_modules/flowbite-react": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/flowbite-react/-/flowbite-react-0.10.2.tgz", + "integrity": "sha512-qkayK6IFmfH7zuuDnHmS0hJxLtL0KpW4vo4i/VQfZ6ZfaNlUsNLQxCGcmXwbZZtUm2WVw8x71aaDOAxftG9tmg==", + "dependencies": { + "@floating-ui/core": "1.6.6", + "@floating-ui/react": "0.26.21", + "classnames": "2.5.1", + "debounce": "2.1.0", + "flowbite": "2.5.1", + "react-icons": "5.2.1", + "tailwind-merge": "2.4.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18", + "tailwindcss": "^3" + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -2655,7 +2806,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -2671,7 +2821,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -2685,7 +2834,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2789,7 +2937,6 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -2809,7 +2956,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -2821,7 +2967,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -2830,7 +2975,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2972,7 +3116,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -3089,7 +3232,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -3138,7 +3280,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -3186,7 +3327,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3210,7 +3350,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -3237,7 +3376,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -3257,11 +3395,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -3427,8 +3569,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/iterator.prototype": { "version": "1.1.5", @@ -3451,7 +3592,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -3466,7 +3606,6 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, "bin": { "jiti": "bin/jiti.js" } @@ -3578,7 +3717,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, "engines": { "node": ">=14" }, @@ -3589,8 +3727,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -3628,8 +3765,7 @@ "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -3644,7 +3780,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } @@ -3664,7 +3799,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -3673,6 +3807,14 @@ "node": ">=8.6" } }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3698,7 +3840,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -3713,7 +3854,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -3827,7 +3967,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3836,7 +3975,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3845,7 +3983,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "engines": { "node": ">= 6" } @@ -4022,8 +4159,7 @@ "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/parent-module": { "version": "1.0.1", @@ -4050,7 +4186,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -4058,14 +4193,12 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -4086,7 +4219,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -4098,7 +4230,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4107,7 +4238,6 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, "engines": { "node": ">= 6" } @@ -4125,7 +4255,6 @@ "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4153,7 +4282,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -4170,7 +4298,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -4189,7 +4316,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4224,7 +4350,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4249,7 +4374,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -4261,8 +4385,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -4306,7 +4429,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -4341,6 +4463,14 @@ "react": "^19.0.0" } }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -4351,7 +4481,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "dependencies": { "pify": "^2.3.0" } @@ -4360,7 +4489,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -4414,7 +4542,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -4452,7 +4579,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -4462,7 +4588,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -4639,7 +4764,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -4651,7 +4775,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -4732,7 +4855,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -4775,7 +4897,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -4793,7 +4914,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -4807,7 +4927,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -4815,14 +4934,12 @@ "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4941,7 +5058,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -4957,7 +5073,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4969,7 +5084,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -5021,7 +5135,6 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -5055,7 +5168,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -5063,11 +5175,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, + "node_modules/tailwind-merge": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.4.0.tgz", + "integrity": "sha512-49AwoOQNKdqKPd9CViyH5wJoSKsCDjUlzL8DxuGp3P1FsGY36NJDAa18jLZcaHAUUuTj+JB8IAo8zWgBNvBF7A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -5104,7 +5229,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5120,7 +5244,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5141,7 +5264,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "dependencies": { "any-promise": "^1.0.0" } @@ -5150,7 +5272,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -5209,7 +5330,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -5232,8 +5352,7 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -5387,14 +5506,12 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -5507,7 +5624,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -5525,7 +5641,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5542,7 +5657,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -5550,14 +5664,12 @@ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5571,7 +5683,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5583,7 +5694,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -5595,7 +5705,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index d92bc1c..6de2e1d 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "earcut": "^3.0.1", + "flowbite-react": "^0.10.2", "next": "15.1.7", "proj4": "^2.15.0", "react": "^19.0.0",