From 4ef94e1b77b29b77df19b765c99c314b93b0af36 Mon Sep 17 00:00:00 2001 From: moddroid94 Date: Wed, 14 Jan 2026 05:02:47 +0100 Subject: [PATCH 01/24] added material-ui as dependency initial Material UI integration migrated model-list cards to Material UI --- frontend/App.tsx | 1263 ++++++++++++++-------------- frontend/components/ModelList.tsx | 292 ++++--- frontend/index.html | 72 +- frontend/package-lock.json | 1287 +++++++++++++++++++++++++++-- frontend/package.json | 3 + 5 files changed, 2082 insertions(+), 835 deletions(-) diff --git a/frontend/App.tsx b/frontend/App.tsx index 0ffe3b6..2f2d5a7 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -21,11 +21,18 @@ import JSZip from "jszip"; import { useMediaQuery } from "./hooks/useMediaQuery"; import { useVisualViewport } from "./hooks/useVisualViewport"; +import { ThemeProvider, createTheme } from "@mui/material/styles"; +import CssBaseline from "@mui/material/CssBaseline"; + const App = () => { const isDesktop = useMediaQuery("(min-width: 1024px)", true); const isMobile = !isDesktop; const visualViewport = useVisualViewport(); - + const darkTheme = createTheme({ + palette: { + mode: "dark", + }, + }); const [folders, setFolders] = useState([]); const [models, setModels] = useState([]); const [storageStats, setStorageStats] = useState({ @@ -527,716 +534,726 @@ const App = () => { }; return ( -
- {isDesktop ? ( - { - setCurrentFolderId(id); - setSelectedModelId(null); - setShowSettings(false); - }} - onCreateFolder={handleCreateFolder} - onRenameFolder={handleRenameFolder} - onDeleteFolder={handleDeleteFolder} - onMoveToFolder={handleDropMove} - onUploadToFolder={(folderId, files) => handleUpload(files, folderId)} - onOpenSettings={() => setShowSettings(true)} - variant="desktop" - /> - ) : ( - <> - setIsMobileSidebarOpen(true)} + + +
+ {isDesktop ? ( + { + setCurrentFolderId(id); + setSelectedModelId(null); + setShowSettings(false); + }} + onCreateFolder={handleCreateFolder} + onRenameFolder={handleRenameFolder} + onDeleteFolder={handleDeleteFolder} + onMoveToFolder={handleDropMove} + onUploadToFolder={(folderId, files) => + handleUpload(files, folderId) + } onOpenSettings={() => setShowSettings(true)} - showMenuButton={!showSettings} + variant="desktop" /> + ) : ( + <> + setIsMobileSidebarOpen(true)} + onOpenSettings={() => setShowSettings(true)} + showMenuButton={!showSettings} + /> - {isMobileSidebarMounted && ( -
-
setIsMobileSidebarOpen(false)} - /> -
- { - setCurrentFolderId(id); - setSelectedModelId(null); - setShowSettings(false); - setIsMobileSidebarOpen(false); - }} - onCreateFolder={handleCreateFolder} - onRenameFolder={handleRenameFolder} - onDeleteFolder={handleDeleteFolder} + {isMobileSidebarMounted && ( +
+
setIsMobileSidebarOpen(false)} + /> +
+ { + setCurrentFolderId(id); + setSelectedModelId(null); + setShowSettings(false); + setIsMobileSidebarOpen(false); + }} + onCreateFolder={handleCreateFolder} + onRenameFolder={handleRenameFolder} + onDeleteFolder={handleDeleteFolder} + onMoveToFolder={handleDropMove} + onUploadToFolder={(folderId, files) => + handleUpload(files, folderId) + } + onOpenSettings={() => { + setShowSettings(true); + setIsMobileSidebarOpen(false); + }} + variant="mobile" + /> +
+
+ )} + + )} + + {/* Settings View */} + {showSettings ? ( + setShowSettings(false)} /> + ) : ( + <> +
+ {isLoading ? ( +
+
+
+

+ Processing... +

+
+
+ ) : ( + handleUpload(files)} + onImport={handleOpenImport} + onSelectModel={(m) => setSelectedModelId(m.id)} + onDelete={handleDeleteModel} + selectedModelId={selectedModelId} + // Selection Props + selectedIds={selectedIds} + onToggleSelection={handleToggleSelection} + onSelectAll={handleSelectAll} + onClearSelection={() => setSelectedIds(new Set())} + onNavigateFolder={(id) => setCurrentFolderId(id)} onMoveToFolder={handleDropMove} onUploadToFolder={(folderId, files) => handleUpload(files, folderId) } - onOpenSettings={() => { - setShowSettings(true); - setIsMobileSidebarOpen(false); - }} - variant="mobile" /> -
-
- )} - - )} - - {/* Settings View */} - {showSettings ? ( - setShowSettings(false)} /> - ) : ( - <> -
- {isLoading ? ( -
-
-
-

Processing...

+ )} + + {/* Upload Indicator */} + {uploadQueue > 0 && ( +
+
+ + Uploading {uploadQueue} file(s)... +
-
- ) : ( - handleUpload(files)} - onImport={handleOpenImport} - onSelectModel={(m) => setSelectedModelId(m.id)} - onDelete={handleDeleteModel} - selectedModelId={selectedModelId} - // Selection Props - selectedIds={selectedIds} - onToggleSelection={handleToggleSelection} - onSelectAll={handleSelectAll} - onClearSelection={() => setSelectedIds(new Set())} - onNavigateFolder={(id) => setCurrentFolderId(id)} - onMoveToFolder={handleDropMove} - onUploadToFolder={(folderId, files) => - handleUpload(files, folderId) - } - /> - )} - - {/* Upload Indicator */} - {uploadQueue > 0 && ( -
-
- - Uploading {uploadQueue} file(s)... - -
- )} - - {/* Backdrop for closing sidebar */} -
setSelectedModelId(null)} - /> + )} - {/* Slide-over panel */} -
- setSelectedModelId(null)} - onUpdate={handleUpdateModel} - onDelete={handleDeleteModel} + {/* Backdrop for closing sidebar */} +
setSelectedModelId(null)} /> -
- - {/* Floating Action Bar - Moved to App to ensure it is top-level Z-index */} - {selectedIds.size > 0 && ( -
-
- - {selectedIds.size} - - selected - -
-
- + {/* Slide-over panel */} +
+ setSelectedModelId(null)} + onUpdate={handleUpdateModel} + onDelete={handleDeleteModel} + /> +
- + selected + +
- +
+ - -
-
- )} + - {/* Modals Layer */} + - {/* Upload Modal */} - {showUploadModal && ( -
-
-
-

- Upload Files -

+
+ )} -
-
-

- {pendingFiles.length} files selected -

-

- {pendingFiles.map((f) => f.name).join(", ")} -

-
- -
- - -
+ {/* Modals Layer */} -
- - setUploadTags(e.target.value)} - /> -

- Separate tags with commas -

-
- -
+ {/* Upload Modal */} + {showUploadModal && ( +
+
+
+

+ Upload + Files +

-
- + +
+
+

+ {pendingFiles.length} files selected +

+

+ {pendingFiles.map((f) => f.name).join(", ")} +

+
+ +
+ + +
+ +
+ + setUploadTags(e.target.value)} + /> +

+ Separate tags with commas +

+
+ +
+ + +
+
+
-
- )} + )} - {/* Import URL Modal */} - {showImportModal && ( -
+ {/* Import URL Modal */} + {showImportModal && (
-
-

- Import from - URL -

- -
- -
-
- - setImportUrl(e.target.value)} - /> -

- Paste a link from Printables or similar sites -

-
- -
- - -
- -
+
+
+

+ Import + from URL +

-
- + +
+
+ + setImportUrl(e.target.value)} + /> +

+ Paste a link from Printables or similar sites +

+
+ +
+ + +
+ +
+ + +
+
+
-
- )} + )} - {/* Import Options Modal */} - {showImportOptionsModal && ( -
+ {/* Import Options Modal */} + {showImportOptionsModal && (
-
-

- Select model - to download -

- -
- - {/* File List */}
900 ? "h-[700px]" : "h-[400px]" - }`} + className="relative bg-vault-800 border border-vault-600 rounded-xl p-6 w-full lg:w-1/2 shadow-2xl animate-in zoom-in-95 duration-200 " + style={{ + maxHeight: Math.max( + 240, + (visualViewport.height || + (typeof window !== "undefined" + ? window.innerHeight + : 0)) - 32 + ), + }} > - {Array.from(folderOptions).map((f) => ( -
-
- {f ? f : "Root Folder"} -
- {modelsOptions.map((model) => ( -
- {model.folder == f ? ( -
- handleOptionsToggleSelection(model.id) - } - className={`group bg-vault-900 border rounded-xl p-4 cursor-pointer transition-all flex items-center gap-4 mb-2 relative overflow-hidden +
+

+ Select + model to download +

+ +
+ + {/* File List */} +
900 ? "h-[700px]" : "h-[400px]" + }`} + > + {Array.from(folderOptions).map((f) => ( +
+
+ {f ? f : "Root Folder"} +
+ {modelsOptions.map((model) => ( +
+ {model.folder == f ? ( +
+ handleOptionsToggleSelection(model.id) + } + className={`group bg-vault-900 border rounded-xl p-4 cursor-pointer transition-all flex items-center gap-4 mb-2 relative overflow-hidden ${ selectedOptions.has(model.id) ? "border-blue-500 ring-1 ring-blue-500/50" : "border-vault-700 hover:border-vault-600" } `} - > -
- {model.name} + > +
+ {model.name} +
+ +
+

+ {model.name} +

+

+ {model.typeName} +

+
+ ) : ( +
+ )} +
+ ))} +
+ ))} +
-
-

- {model.name} -

-

- {model.typeName} -

-
-
- ) : ( -
- )} -
- ))} -
- ))} -
- -
handleImportChoice()} - className="static bottom-0 p-2 mt-4 cursor-pointer rounded-lg bg-vault-700 hover:bg-vault-600 text-slate-200 font-medium transition-colors text-center" - > - {" "} - Import{" "} +
handleImportChoice()} + className="static bottom-0 p-2 mt-4 cursor-pointer rounded-lg bg-vault-700 hover:bg-vault-600 text-slate-200 font-medium transition-colors text-center" + > + {" "} + Import{" "} +
-
- )} + )} - {/* Delete Confirmation Modal */} - {deleteConfirmState.isOpen && ( -
+ {/* Delete Confirmation Modal */} + {deleteConfirmState.isOpen && (
-
-
- +
+
+
+ +
+

+ Confirm Deletion +

+

+ {deleteConfirmState.type === "single" && + "Are you sure you want to delete this model? This action cannot be undone."} + {deleteConfirmState.type === "bulk" && + `Are you sure you want to delete ${selectedIds.size} models? This action cannot be undone.`} + {deleteConfirmState.type === "folder" && + "Are you sure you want to delete this folder?"} +

-

- Confirm Deletion -

-

- {deleteConfirmState.type === "single" && - "Are you sure you want to delete this model? This action cannot be undone."} - {deleteConfirmState.type === "bulk" && - `Are you sure you want to delete ${selectedIds.size} models? This action cannot be undone.`} - {deleteConfirmState.type === "folder" && - "Are you sure you want to delete this folder?"} -

-
-
- - +
+ + +
-
- )} + )} - {showMoveModal && ( -
+ {showMoveModal && (
-
-

- Move to Folder -

- -
-
- {folders.map((folder) => ( +
+
+

+ Move to Folder +

- ))} +
+
+ {folders.map((folder) => ( + + ))} +
-
- )} + )} - {showTagModal && ( -
+ {showTagModal && (
-
-

- Add Tags -

- -
-
-

- Add tags to {selectedIds.size} items (comma separated): -

- setBulkTags(e.target.value)} - /> -
+
+
+

+ Add Tags +

-
- +
+

+ Add tags to {selectedIds.size} items (comma separated): +

+ setBulkTags(e.target.value)} + /> +
+ + +
+
+
-
- )} -
- - )} -
+ )} + + + )} +
+ ); }; diff --git a/frontend/components/ModelList.tsx b/frontend/components/ModelList.tsx index 4210458..5ac7489 100644 --- a/frontend/components/ModelList.tsx +++ b/frontend/components/ModelList.tsx @@ -12,10 +12,28 @@ import { Download, Globe, Folder as FolderIcon, + DownloadIcon, + ScreenShareIcon, } from "lucide-react"; import { STLModel, Folder } from "../types"; import { api } from "../services/api"; +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import CardMedia from "@mui/material/CardMedia"; +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import CardActionArea from "@mui/material/CardActionArea"; +import CardActions from "@mui/material/CardActions"; +import Chip from "@mui/material/Chip"; +import { String } from "three/examples/jsm/transpiler/AST.js"; +import { IconButton } from "@mui/material"; +import Tooltip from "@mui/material/Tooltip"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import Divider from "@mui/material/Divider"; +import Checkbox from "@mui/material/Checkbox"; + interface ModelListProps { models: STLModel[]; folders: Folder[]; @@ -71,12 +89,9 @@ const ModelList: React.FC = ({ const [dragOverFolderId, setDragOverFolderId] = useState(null); const [isTouchDevice, setIsTouchDevice] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + // Close menu when clicking outside - useEffect(() => { - const handleClickOutside = () => setActiveMenuModelId(null); - window.addEventListener("click", handleClickOutside); - return () => window.removeEventListener("click", handleClickOutside); - }, []); useEffect(() => { if (typeof window === "undefined") return; @@ -345,7 +360,8 @@ const ModelList: React.FC = ({
) : (
-
+ {/* Folders */} +
{/* Render Folders First */} {processedFolders.map((folder) => (
= ({
))}
-
+ {/* Files */} +
{/* Render Models */} {processedModels.map((model) => { const isSelected = selectedIds.has(model.id); @@ -396,7 +413,6 @@ const ModelList: React.FC = ({ key={model.id} draggable={true} onDragStart={(e) => handleCardDragStart(e, model.id)} - style={{ zIndex: isMenuOpen ? 20 : "auto" }} onClick={() => { if (selectionMode) { onToggleSelection(model.id); @@ -404,142 +420,168 @@ const ModelList: React.FC = ({ onSelectModel(model); } }} - className={`group bg-vault-900 border rounded-xl p-4 cursor-pointer transition-all duration-200 hover:shadow-lg hover:-translate-y-1 relative active:cursor-grabbing ${ + className={`group cursor-pointer transition-all duration-200 hover:shadow-lg hover:-translate-y-1 relative active:cursor-grabbing ${ isSelected || selectedModelId === model.id ? "border-blue-500 ring-1 ring-blue-500/50" : "border-vault-700 hover:border-vault-600" }`} > - {/* Selection Checkbox */} -
{ - e.stopPropagation(); - onToggleSelection(model.id); - }} - className={`absolute top-4 left-4 z-10 rounded bg-vault-900/80 backdrop-blur-sm transition-opacity duration-200 p-1 - ${ - isSelected || selectionMode - ? "opacity-100" - : "opacity-0 group-hover:opacity-100" - }`} - > - {isSelected ? ( - - ) : ( - - )} -
- -
- {/* Thumbnail or Placeholder */} - {model.thumbnail ? ( - {model.name} - ) : ( - <> -
- - - )} - - {/* Badges */} -
- {model.tags.slice(0, 2).map((tag) => ( - - {tag} - - ))} - {model.tags.length > 2 && ( - - +{model.tags.length - 2} - + + + {model.thumbnail ? ( + + ) : ( + <> +
+ + )} -
- - {/* File Type Badge */} -
- - {model.name.split(".").pop()} - -
-
- -
-
-

- {model.name} -

-
- - {(model.size / (1024 * 1024)).toFixed(2)} MB - - β€’ - - {new Date(model.dateAdded).toLocaleDateString()} - +
+ {model.tags.slice(0, 2).map((tag) => ( + + ))} + {model.tags.length > 2 && ( + + )}
-
- -
- - - {/* Dropdown Menu */} - {isMenuOpen && ( -
-
+ + + {model.name} + + + {(model.size / (1024 * 1024)).toFixed(2)} + {" MB β€’ "} + {new Date(model.dateAdded).toLocaleDateString()} + + + + + + + + + + + + + + +
+ { + e.stopPropagation(); + setAnchorEl(e.currentTarget); + setActiveMenuModelId(isMenuOpen ? null : model.id); + }} + > + + + { + e.stopPropagation(); + setActiveMenuModelId(null); + }} + anchorOrigin={{ + vertical: "top", + horizontal: "right", + }} + transformOrigin={{ + vertical: "top", + horizontal: "right", + }} + > + { - e.stopPropagation(); onSelectModel(model); setActiveMenuModelId(null); }} - className="w-full text-left px-3 py-2 text-sm text-slate-300 hover:bg-vault-700 hover:text-white flex items-center gap-2" > - Open - - - { - e.stopPropagation(); - setActiveMenuModelId(null); - }} - className="w-full text-left px-3 py-2 text-sm text-slate-300 hover:bg-vault-700 hover:text-white flex items-center gap-2" - > - Download - - - -
- )} -
+ Delete + + +
+ + + {/* Selection Checkbox */} +
{ + e.stopPropagation(); + onToggleSelection(model.id); + }} + className={`absolute top-2 left-2 z-10 rounded backdrop-blur-sm transition-opacity duration-200 + ${ + isSelected || selectionMode + ? "opacity-100" + : "opacity-0 group-hover:opacity-100" + }`} + > +
); diff --git a/frontend/index.html b/frontend/index.html index 61c5f1c..a9fdbae 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,10 +3,24 @@ - - - - + + + + STL Vault - - + + + + +
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 59c73b9..54b527a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,13 +1,16 @@ { "name": "stlvault", - "version": "0.4.0", + "version": "0.4.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "stlvault", - "version": "0.0.1", + "version": "0.4.2", "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/material": "^7.3.7", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.4.0", "jszip": "^3.10.1", @@ -29,7 +32,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", @@ -82,7 +84,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", - "dev": true, "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", @@ -114,7 +115,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -123,7 +123,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" @@ -162,7 +161,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -171,7 +169,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -202,7 +199,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, "dependencies": { "@babel/types": "^7.28.5" }, @@ -255,7 +251,6 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", @@ -269,7 +264,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -287,7 +281,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -301,6 +294,158 @@ "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==" }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", @@ -721,7 +866,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -741,7 +885,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" } @@ -749,14 +892,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -778,6 +919,223 @@ "three": ">= 0.159.0" } }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.7.tgz", + "integrity": "sha512-8jWwS6FweMkpyRkrJooamUGe1CQfO1yJ+lM43IyUJbrhHW/ObES+6ry4vfGi8EKaldHL3t3BG1bcLcERuJPcjg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.7.tgz", + "integrity": "sha512-6bdIxqzeOtBAj2wAsfhWCYyMKPLkRO9u/2o5yexcL0C3APqyy91iGSWgT3H7hg+zR2XgE61+WAu12wXPON8b6A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/core-downloads-tracker": "^7.3.7", + "@mui/system": "^7.3.7", + "@mui/types": "^7.4.10", + "@mui/utils": "^7.3.7", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.3", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.3.7", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.7.tgz", + "integrity": "sha512-w7r1+CYhG0syCAQUWAuV5zSaU2/67WA9JXUderdb7DzCIJdp/5RmJv6L85wRjgKCMsxFF0Kfn0kPgPbPgw/jdw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.7", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.7.tgz", + "integrity": "sha512-y/QkNXv6cF6dZ5APztd/dFWfQ6LHKPx3skyYO38YhQD4+Cxd6sFAL3Z38WMSSC8LQz145Mpp3CcLrSCLKPwYAg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.7.tgz", + "integrity": "sha512-DovL3k+FBRKnhmatzUMyO5bKkhMLlQ9L7Qw5qHrre3m8zCZmE+31NDVBFfqrbrA7sq681qaEIHdkWD5nmiAjyQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/private-theming": "^7.3.7", + "@mui/styled-engine": "^7.3.7", + "@mui/types": "^7.4.10", + "@mui/utils": "^7.3.7", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.10.tgz", + "integrity": "sha512-0+4mSjknSu218GW3isRqoxKRTOrTLd/vHi/7UC4+wZcUrOAqD9kRk7UQRL1mcrzqRoe7s3UT6rsRpbLkW5mHpQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.7.tgz", + "integrity": "sha512-+YjnjMRnyeTkWnspzoxRdiSOgkrcpTikhNPoxOZW0APXx+urHtUoXJ9lbtCZRCA5a4dg5gSbd19alL1DvRs5fg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/types": "^7.4.10", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@react-three/drei": { "version": "10.7.7", "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz", @@ -1229,6 +1587,18 @@ "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==" }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", @@ -1246,6 +1616,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/stats.js": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", @@ -1311,6 +1690,21 @@ "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.66.tgz", "integrity": "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==" }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1403,6 +1797,15 @@ "ieee754": "^1.2.1" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camera-controls": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", @@ -1435,6 +1838,15 @@ } ] }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1446,6 +1858,31 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -1485,7 +1922,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -1506,6 +1942,16 @@ "webgl-constants": "^1.1.1" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/draco3d": { "version": "1.5.7", "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", @@ -1517,6 +1963,15 @@ "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", "dev": true }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/esbuild": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", @@ -1567,6 +2022,18 @@ "node": ">=6" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1589,6 +2056,12 @@ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1603,6 +2076,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -1617,11 +2099,38 @@ "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==" }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hls.js": { "version": "1.6.15", "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz", "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==" }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1646,11 +2155,48 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -1688,14 +2234,12 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -1703,6 +2247,12 @@ "node": ">=6" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -1734,6 +2284,24 @@ "immediate": "~3.0.5" } }, + "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==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -1776,8 +2344,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { "version": "3.3.11", @@ -1803,11 +2370,50 @@ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1816,11 +2422,25 @@ "node": ">=8" } }, + "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==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "4.0.3", @@ -1881,6 +2501,23 @@ "lie": "^3.0.2" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", @@ -1905,6 +2542,12 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" }, + "node_modules/react-is": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", + "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", + "license": "MIT" + }, "node_modules/react-reconciler": { "version": "0.31.0", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz", @@ -1928,6 +2571,22 @@ "node": ">=0.10.0" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/react-use-measure": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", @@ -1969,6 +2628,35 @@ "node": ">=0.10.0" } }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/rollup": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", @@ -2048,6 +2736,15 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2093,6 +2790,24 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-preserve-symlinks-flag": { + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/suspend-react": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", @@ -2399,6 +3114,24 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/zustand": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", @@ -2433,7 +3166,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", @@ -2473,7 +3205,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", - "dev": true, "requires": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", @@ -2498,14 +3229,12 @@ "@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" }, "@babel/helper-module-imports": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, "requires": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" @@ -2531,14 +3260,12 @@ "@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" }, "@babel/helper-validator-identifier": { "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==" }, "@babel/helper-validator-option": { "version": "7.27.1", @@ -2560,7 +3287,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, "requires": { "@babel/types": "^7.28.5" } @@ -2592,7 +3318,6 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, "requires": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", @@ -2603,7 +3328,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2618,7 +3342,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, "requires": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -2629,6 +3352,127 @@ "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==" }, + "@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + } + } + }, + "@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "requires": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, + "@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "requires": { + "@emotion/memoize": "^0.9.0" + } + }, + "@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "requires": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + }, + "@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + } + }, + "@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + }, + "@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, "@esbuild/aix-ppc64": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", @@ -2815,7 +3659,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -2834,20 +3677,17 @@ "@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" }, "@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2866,6 +3706,94 @@ "promise-worker-transferable": "^1.0.4" } }, + "@mui/core-downloads-tracker": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.7.tgz", + "integrity": "sha512-8jWwS6FweMkpyRkrJooamUGe1CQfO1yJ+lM43IyUJbrhHW/ObES+6ry4vfGi8EKaldHL3t3BG1bcLcERuJPcjg==" + }, + "@mui/material": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.7.tgz", + "integrity": "sha512-6bdIxqzeOtBAj2wAsfhWCYyMKPLkRO9u/2o5yexcL0C3APqyy91iGSWgT3H7hg+zR2XgE61+WAu12wXPON8b6A==", + "requires": { + "@babel/runtime": "^7.28.4", + "@mui/core-downloads-tracker": "^7.3.7", + "@mui/system": "^7.3.7", + "@mui/types": "^7.4.10", + "@mui/utils": "^7.3.7", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.3", + "react-transition-group": "^4.4.5" + } + }, + "@mui/private-theming": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.7.tgz", + "integrity": "sha512-w7r1+CYhG0syCAQUWAuV5zSaU2/67WA9JXUderdb7DzCIJdp/5RmJv6L85wRjgKCMsxFF0Kfn0kPgPbPgw/jdw==", + "requires": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.7", + "prop-types": "^15.8.1" + } + }, + "@mui/styled-engine": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.7.tgz", + "integrity": "sha512-y/QkNXv6cF6dZ5APztd/dFWfQ6LHKPx3skyYO38YhQD4+Cxd6sFAL3Z38WMSSC8LQz145Mpp3CcLrSCLKPwYAg==", + "requires": { + "@babel/runtime": "^7.28.4", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + } + }, + "@mui/system": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.7.tgz", + "integrity": "sha512-DovL3k+FBRKnhmatzUMyO5bKkhMLlQ9L7Qw5qHrre3m8zCZmE+31NDVBFfqrbrA7sq681qaEIHdkWD5nmiAjyQ==", + "requires": { + "@babel/runtime": "^7.28.4", + "@mui/private-theming": "^7.3.7", + "@mui/styled-engine": "^7.3.7", + "@mui/types": "^7.4.10", + "@mui/utils": "^7.3.7", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + } + }, + "@mui/types": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.10.tgz", + "integrity": "sha512-0+4mSjknSu218GW3isRqoxKRTOrTLd/vHi/7UC4+wZcUrOAqD9kRk7UQRL1mcrzqRoe7s3UT6rsRpbLkW5mHpQ==", + "requires": { + "@babel/runtime": "^7.28.4" + } + }, + "@mui/utils": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.7.tgz", + "integrity": "sha512-+YjnjMRnyeTkWnspzoxRdiSOgkrcpTikhNPoxOZW0APXx+urHtUoXJ9lbtCZRCA5a4dg5gSbd19alL1DvRs5fg==", + "requires": { + "@babel/runtime": "^7.28.4", + "@mui/types": "^7.4.10", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.3" + } + }, + "@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" + }, "@react-three/drei": { "version": "10.7.7", "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz", @@ -3144,6 +4072,16 @@ "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==" }, + "@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, + "@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==" + }, "@types/react": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", @@ -3158,6 +4096,12 @@ "integrity": "sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA==", "requires": {} }, + "@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "requires": {} + }, "@types/stats.js": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", @@ -3214,6 +4158,16 @@ "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.66.tgz", "integrity": "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==" }, + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3255,6 +4209,11 @@ "ieee754": "^1.2.1" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, "camera-controls": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", @@ -3267,6 +4226,11 @@ "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", "dev": true }, + "clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" + }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3278,6 +4242,25 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + } + } + }, "cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -3305,7 +4288,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "requires": { "ms": "^2.1.3" } @@ -3318,6 +4300,15 @@ "webgl-constants": "^1.1.1" } }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "draco3d": { "version": "1.5.7", "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", @@ -3329,9 +4320,16 @@ "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", "dev": true }, + "error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, "esbuild": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", + "version": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "requires": { @@ -3369,6 +4367,11 @@ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, "fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3381,6 +4384,11 @@ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3388,6 +4396,11 @@ "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3399,11 +4412,34 @@ "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==" }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, "hls.js": { "version": "1.6.15", "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz", "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==" }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3414,11 +4450,33 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, + "import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "requires": { + "hasown": "^2.0.2" + } + }, "is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -3453,14 +4511,17 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json5": { "version": "2.2.3", @@ -3487,6 +4548,19 @@ "immediate": "~3.0.5" } }, + "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==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3522,8 +4596,7 @@ "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "nanoid": { "version": "3.3.11", @@ -3537,21 +4610,54 @@ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, + "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==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { "version": "4.0.3", @@ -3589,6 +4695,23 @@ "lie": "^3.0.2" } }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", @@ -3609,6 +4732,11 @@ } } }, + "react-is": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", + "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==" + }, "react-reconciler": { "version": "0.31.0", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz", @@ -3623,6 +4751,17 @@ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", "dev": true }, + "react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, "react-use-measure": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", @@ -3655,6 +4794,21 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, + "resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "requires": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, "rollup": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", @@ -3716,6 +4870,11 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + }, "source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3758,6 +4917,16 @@ } } }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "supports-preserve-symlinks-flag": { + "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==" + }, "suspend-react": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", @@ -3927,6 +5096,14 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "optional": true, + "peer": true + }, "zustand": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index 19cad41..236cbc0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,9 @@ "preview": "vite preview" }, "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/material": "^7.3.7", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.4.0", "jszip": "^3.10.1", From eac731de5f1fdb2b660648b3b2e3f3b4cecde5bc Mon Sep 17 00:00:00 2001 From: moddroid94 Date: Wed, 14 Jan 2026 18:33:12 +0100 Subject: [PATCH 02/24] migrated folders to Material UI fixed drag n' drop layout / functionality --- frontend/components/ModelList.tsx | 100 ++++++++++++++++++------------ 1 file changed, 59 insertions(+), 41 deletions(-) diff --git a/frontend/components/ModelList.tsx b/frontend/components/ModelList.tsx index 5ac7489..21edec6 100644 --- a/frontend/components/ModelList.tsx +++ b/frontend/components/ModelList.tsx @@ -33,6 +33,8 @@ import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import Divider from "@mui/material/Divider"; import Checkbox from "@mui/material/Checkbox"; +import Avatar from "@mui/material/Avatar"; +import Stack from "@mui/material/Stack"; interface ModelListProps { models: STLModel[]; @@ -229,26 +231,7 @@ const ModelList: React.FC = ({ const selectionMode = selectedIds.size > 0; return ( -
- {/* Drag Overlay */} - {isDragging && ( -
-
- -

- Drop 3D files here -

-

Supported: STL, STEP, 3MF

-
-
- )} - +
{/* Header Section */}
@@ -378,31 +361,66 @@ const ModelList: React.FC = ({ setDragOverFolderId(null); }} onDrop={(e) => handleFolderDrop(e, folder.id)} - className={`group bg-vault-900 border rounded-xl p-4 cursor-pointer transition-all flex items-center gap-4 relative overflow-hidden - ${ - dragOverFolderId === folder.id - ? "border-blue-400 bg-blue-900/10 ring-1 ring-blue-400" - : "border-vault-700 hover:border-vault-600 hover:bg-vault-800/50" - } - `} + className={`cursor-pointer transition-all flex items-center relative overflow-hidden hover:-translate-y-1 ${ + dragOverFolderId === folder.id + ? " -translate-y-1 brightness-150" + : " " + }`} > -
- -
-
-

- {folder.name} -

-

Folder

-
- {dragOverFolderId === folder.id && ( -
- )} + + + + + + + + + + {folder.name} + + + Folder + + + + + +
))}
{/* Files */} -
+
+ {/* Drag Overlay */} + {isDragging && ( +
+
+ +

+ Drop 3D files +

+

+ Supported: STL, STEP, 3MF +

+
+
+ )} {/* Render Models */} {processedModels.map((model) => { const isSelected = selectedIds.has(model.id); @@ -440,7 +458,7 @@ const ModelList: React.FC = ({ )} -
+
{model.tags.slice(0, 2).map((tag) => ( Date: Wed, 14 Jan 2026 21:49:06 +0100 Subject: [PATCH 03/24] migrated search and sort to Material UI migrated checkboxes to Material UI --- frontend/components/ModelList.tsx | 86 +++++++++++++++++++------------ 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/frontend/components/ModelList.tsx b/frontend/components/ModelList.tsx index 21edec6..43c4c03 100644 --- a/frontend/components/ModelList.tsx +++ b/frontend/components/ModelList.tsx @@ -3,17 +3,15 @@ import { CloudUpload, FileBox, Search, - ArrowUpDown, CheckSquare, - Square, MoreVertical, - Trash2, ExternalLink, Download, Globe, Folder as FolderIcon, DownloadIcon, ScreenShareIcon, + XCircle, } from "lucide-react"; import { STLModel, Folder } from "../types"; import { api } from "../services/api"; @@ -27,7 +25,7 @@ import CardActionArea from "@mui/material/CardActionArea"; import CardActions from "@mui/material/CardActions"; import Chip from "@mui/material/Chip"; import { String } from "three/examples/jsm/transpiler/AST.js"; -import { IconButton } from "@mui/material"; +import IconButton from "@mui/material/IconButton"; import Tooltip from "@mui/material/Tooltip"; import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; @@ -35,6 +33,11 @@ import Divider from "@mui/material/Divider"; import Checkbox from "@mui/material/Checkbox"; import Avatar from "@mui/material/Avatar"; import Stack from "@mui/material/Stack"; +import TextField from "@mui/material/TextField"; +import InputAdornment from "@mui/material/InputAdornment"; +import FormControl from "@mui/material/FormControl"; +import Select, { SelectChangeEvent } from "@mui/material/Select"; +import InputLabel from "@mui/material/InputLabel"; interface ModelListProps { models: STLModel[]; @@ -93,8 +96,6 @@ const ModelList: React.FC = ({ const [anchorEl, setAnchorEl] = React.useState(null); - // Close menu when clicking outside - useEffect(() => { if (typeof window === "undefined") return; const isTouch = @@ -283,32 +284,57 @@ const ModelList: React.FC = ({
{/* Search & Sort Bar */} -
+
- - setSearchQuery(e.target.value)} + slotProps={{ + input: { + startAdornment: ( + + + + ), + endAdornment: ( + + { + setSearchQuery(""); + document.getElementById("search-input").value = ""; + }} + edge="end" + > + + + + ), + }, + }} + variant="outlined" />
- - + + Sort + +
@@ -438,13 +464,9 @@ const ModelList: React.FC = ({ onSelectModel(model); } }} - className={`group cursor-pointer transition-all duration-200 hover:shadow-lg hover:-translate-y-1 relative active:cursor-grabbing ${ - isSelected || selectedModelId === model.id - ? "border-blue-500 ring-1 ring-blue-500/50" - : "border-vault-700 hover:border-vault-600" - }`} + className={`group cursor-pointer transition-all duration-200 hover:shadow-lg hover:-translate-y-1 relative active:cursor-grabbing`} > - + {model.thumbnail ? ( Date: Wed, 14 Jan 2026 22:44:10 +0100 Subject: [PATCH 04/24] migrated header section of model list to Material UI fixed background color to follow Material theme --- frontend/App.tsx | 2 +- frontend/components/ModelList.tsx | 116 +++++++++++++++++++----------- frontend/index.html | 2 +- 3 files changed, 76 insertions(+), 44 deletions(-) diff --git a/frontend/App.tsx b/frontend/App.tsx index 2f2d5a7..2ecfedd 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -539,7 +539,7 @@ const App = () => {
{isDesktop ? ( = ({ const [anchorEl, setAnchorEl] = React.useState(null); + const VisuallyHiddenInput = styled("input")({ + clip: "rect(0 0 0 0)", + clipPath: "inset(50%)", + height: 1, + overflow: "hidden", + position: "absolute", + bottom: 0, + left: 0, + whiteSpace: "nowrap", + width: 1, + }); + useEffect(() => { if (typeof window === "undefined") return; const isTouch = @@ -232,54 +245,67 @@ const ModelList: React.FC = ({ const selectionMode = selectedIds.size > 0; return ( -
+
{/* Header Section */}
-

- Model Library -

-

- {processedFolders.length}{" "} - {processedFolders.length === 1 ? "folder - " : "folders - "} - {processedModels.length}{" "} - {processedModels.length === 1 ? "model" : "models"} - {models.length !== processedModels.length && - ` (filtered from ${models.length})`} -

+ + Model Library + + {processedFolders.length}{" "} + {processedFolders.length === 1 ? "folder β€’ " : "folders β€’ "} + {processedModels.length}{" "} + {processedModels.length === 1 ? "model" : "models"} + {models.length !== processedModels.length && + ` ( filtered from: ${models.length} )`} + +
- - - - + + + + +
@@ -301,11 +327,15 @@ const ModelList: React.FC = ({ endAdornment: ( { setSearchQuery(""); document.getElementById("search-input").value = ""; }} - edge="end" > @@ -425,6 +455,7 @@ const ModelList: React.FC = ({
))}
+ {/* Files */}
= ({
)} + {/* Render Models */} {processedModels.map((model) => { const isSelected = selectedIds.has(model.id); diff --git a/frontend/index.html b/frontend/index.html index a9fdbae..f38118e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -54,7 +54,7 @@ href="https://fonts.googleapis.com/icon?family=Material+Icons" /> - +
From 12ea4a16f012d6f30dc4a1988208fa3d44659159 Mon Sep 17 00:00:00 2001 From: moddroid94 Date: Thu, 15 Jan 2026 15:49:40 +0100 Subject: [PATCH 05/24] Added Material UI X as dependency Migrated Treeview to MUI X TreeView feature parity with old tree, but different controls. Notable Changes: - Edit label is now on Double click - Creating subfolders now uses root folder input field (not spawning a new field under the current folder anymore) - Removed count badge as it seemed useless (could be reintegrated) - Edit buttons now truncate the folder label instead of overlapping it, and they are permanently visible -Expansion of the tree now is only triggered by the expansion icon, selection is triggered by label click - Expansion of the tree does not yet follow the current folder we're in. (it needs the imperative API to control the tree, but it's quite some work and alot of code, deemed unnecessary for now) --- frontend/components/ModelList.tsx | 2 +- frontend/components/Sidebar.tsx | 520 +++++++++++------------------- frontend/package-lock.json | 143 ++++++++ frontend/package.json | 1 + 4 files changed, 336 insertions(+), 330 deletions(-) diff --git a/frontend/components/ModelList.tsx b/frontend/components/ModelList.tsx index bd9ec4d..8832dfb 100644 --- a/frontend/components/ModelList.tsx +++ b/frontend/components/ModelList.tsx @@ -419,7 +419,7 @@ const ModelList: React.FC = ({ onDrop={(e) => handleFolderDrop(e, folder.id)} className={`cursor-pointer transition-all flex items-center relative overflow-hidden hover:-translate-y-1 ${ dragOverFolderId === folder.id - ? " -translate-y-1 brightness-150" + ? " -translate-y-1 brightness-150 ring-2 ring-white rounded-md" : " " }`} > diff --git a/frontend/components/Sidebar.tsx b/frontend/components/Sidebar.tsx index 132bda4..967c12a 100644 --- a/frontend/components/Sidebar.tsx +++ b/frontend/components/Sidebar.tsx @@ -10,9 +10,27 @@ import { X, ChevronRight, Settings, + PlusIcon, } from "lucide-react"; import { Folder, STLModel, StorageStats } from "../types"; +import Stack from "@mui/material/Stack"; +import IconButton from "@mui/material/IconButton"; +import Typography from "@mui/material/Typography"; +import { TreeViewDefaultItemModelProperties } from "@mui/x-tree-view/models"; +import { useTreeItemUtils } from "@mui/x-tree-view/hooks"; +import { + UseTreeItemContentSlotOwnProps, + UseTreeItemLabelSlotOwnProps, + UseTreeItemStatus, +} from "@mui/x-tree-view/useTreeItem"; +import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; +import { + TreeItem, + TreeItemProps, + TreeItemSlotProps, +} from "@mui/x-tree-view/TreeItem"; + const APP_TAG = import.meta.env.VITE_APP_TAG || __APP_VERSION__ || "dev"; interface SidebarProps { @@ -30,287 +48,6 @@ interface SidebarProps { variant?: "desktop" | "mobile"; } -// Helper component for recursive rendering -interface FolderNodeProps { - folder: Folder; - level: number; - allFolders: Folder[]; - currentFolderId: string; - expandedIds: Set; - editingId: string | null; - dragTargetId: string | null; - folderCounts: Record; - creatingSubfolderId: string | null; - // Callbacks - onToggleExpand: (id: string) => void; - onSelect: (id: string) => void; - onRename: (id: string, name: string) => void; - onDelete: (id: string, count: number, hasChildren: boolean) => void; - onSetEditing: (id: string | null) => void; - onSetCreatingSubfolder: (id: string | null) => void; - onCreateSubfolder: (name: string, parentId: string) => void; - onDragOver: (e: React.DragEvent, id: string) => void; - onDragLeave: (e: React.DragEvent) => void; - onDrop: (e: React.DragEvent, id: string) => void; -} - -const FolderNode: React.FC = ({ - folder, - level, - allFolders, - currentFolderId, - expandedIds, - editingId, - dragTargetId, - folderCounts, - creatingSubfolderId, - onToggleExpand, - onSelect, - onRename, - onDelete, - onSetEditing, - onSetCreatingSubfolder, - onCreateSubfolder, - onDragOver, - onDragLeave, - onDrop, -}) => { - const [editName, setEditName] = useState(folder.name); - const [subfolderName, setSubfolderName] = useState(""); - - const children = allFolders.filter((f) => f.parentId === folder.id); - const hasChildren = children.length > 0; - const isExpanded = expandedIds.has(folder.id); - const isEditing = editingId === folder.id; - const isCreatingChild = creatingSubfolderId === folder.id; - const isDropTarget = dragTargetId === folder.id; - const count = folderCounts[folder.id] || 0; - const isSelected = currentFolderId === folder.id; - - // Reset edit name when starting edit - React.useEffect(() => { - if (isEditing) setEditName(folder.name); - }, [isEditing, folder.name]); - - const handleRenameSubmit = (e: React.FormEvent) => { - e.preventDefault(); - e.stopPropagation(); - if (editName.trim()) { - onRename(folder.id, editName.trim()); - onSetEditing(null); - } - }; - - const handleSubfolderSubmit = (e: React.FormEvent) => { - e.preventDefault(); - e.stopPropagation(); - if (subfolderName.trim()) { - onCreateSubfolder(subfolderName.trim(), folder.id); - setSubfolderName(""); - onSetCreatingSubfolder(null); - // Ensure we are expanded to see the new child - if (!isExpanded) onToggleExpand(folder.id); - } - }; - - return ( -
- {/* Folder Row */} -
onDragOver(e, folder.id)} - onDragLeave={onDragLeave} - onDrop={(e) => onDrop(e, folder.id)} - > - {/* Expand Toggle */} -
{ - e.stopPropagation(); - onToggleExpand(folder.id); - }} - > - {hasChildren ? ( - - ) : ( - // Placeholder to keep alignment -
- )} -
- - {/* Content */} - {isEditing ? ( -
- setEditName(e.target.value)} - onClick={(e) => e.stopPropagation()} - onKeyDown={(e) => e.key === "Escape" && onSetEditing(null)} - /> - - -
- ) : ( - <> -
onSelect(folder.id)} - > - - - {folder.name} - -
- - {/* Hover Actions */} -
- - - -
- - {/* Count Badge */} - - {count} - - - )} -
- - {/* New Subfolder Input */} - {isCreatingChild && ( -
-
- - setSubfolderName(e.target.value)} - onKeyDown={(e) => - e.key === "Escape" && onSetCreatingSubfolder(null) - } - /> - - - -
- )} - - {/* Children */} - {isExpanded && ( -
- {children.map((child) => ( - - ))} -
- )} -
- ); -}; - const Sidebar: React.FC = ({ folders, models, @@ -350,6 +87,18 @@ const Sidebar: React.FC = ({ [isDesktopVariant] ); + // Calculate direct counts only (not recursive, matching file system behavior usually) + const folderCounts = useMemo(() => { + const counts: Record = {}; + models.forEach((m) => { + counts[m.folderId] = (counts[m.folderId] || 0) + 1; + }); + folders.forEach((f) => { + counts[f.parentId] = (counts[f.parentId] || 0) + 1; + }); + return counts; + }, [models, folders]); + useEffect(() => { if (!isDesktopVariant) return; if (!isResizing) return; @@ -377,20 +126,8 @@ const Sidebar: React.FC = ({ }; }, [isResizing, isDesktopVariant]); - // Calculate direct counts only (not recursive, matching file system behavior usually) - const folderCounts = useMemo(() => { - const counts: Record = {}; - models.forEach((m) => { - counts[m.folderId] = (counts[m.folderId] || 0) + 1; - }); - folders.forEach((f) => { - counts[f.parentId] = (counts[f.parentId] || 0) + 1; - }); - return counts; - }, [models, folders]); - // Ensure parents of current folder are expanded - React.useEffect(() => { + useEffect(() => { if (currentFolderId && currentFolderId !== "all") { const expandPath = (id: string, path: Set) => { const folder = folders.find((f) => f.id === id); @@ -408,12 +145,17 @@ const Sidebar: React.FC = ({ } }, [currentFolderId, folders]); - const handleCreateRootSubmit = (e: React.FormEvent) => { + const handleCreateFolderSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (newRootName.trim()) { + if (newRootName.trim() && !creatingSubfolderId) { onCreateFolder(newRootName.trim(), null); setNewRootName(""); setIsCreatingRoot(false); + } else if (newRootName.trim() && creatingSubfolderId != "") { + onCreateFolder(newRootName.trim(), creatingSubfolderId); + setNewRootName(""); + setIsCreatingRoot(false); + setCreatingSubfolderId(""); } }; @@ -427,12 +169,13 @@ const Sidebar: React.FC = ({ }); }; - const handleDeleteRequest = ( - id: string, - count: number, - hasChildren: boolean - ) => { - if (count > 0 || hasChildren) { + const handleExpand = (e) => { + e.stopPropagation(); + e.preventDefault(); + }; + + const handleDeleteRequest = (id: string, count: number) => { + if (count > 0) { alert("Folder must be empty to delete (no files and no subfolders)."); return; } @@ -495,6 +238,132 @@ const Sidebar: React.FC = ({ // Root folders const rootFolders = folders.filter((f) => f.parentId === null); + //builds the treeview structure + const treefolders = () => { + const treeitems: TreeViewDefaultItemModelProperties[] = []; + rootFolders.map((folder) => { + treeitems.push({ + id: folder.id, + label: folder.name, + children: [], + }); + }); + treeitems.map((folder) => { + folders.map((subfolder) => { + if (subfolder.parentId === folder.id) { + folder.children.push({ id: subfolder.id, label: subfolder.name }); + } + }); + folder.children.sort((a, b) => { + return a.label.localeCompare(b.label); + }); + }); + treeitems.sort((a, b) => { + return a.label.localeCompare(b.label); + }); + return treeitems; + }; + + interface CustomLabelProps extends UseTreeItemLabelSlotOwnProps { + status: UseTreeItemStatus; + onClick: React.MouseEventHandler; + onPlusClick: React.MouseEventHandler; + } + + function CustomLabel({ + children, + status, + onClick, + onPlusClick, + ...props + }: CustomLabelProps) { + return ( + + {children} + + + + + + + + + + ); + } + + const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: TreeItemProps, + ref: React.Ref + ) { + const { interactions, status } = useTreeItemUtils({ + itemId: props.itemId, + children: props.children, + }); + + const handleContentClick: UseTreeItemContentSlotOwnProps["onClick"] = ( + event + ) => { + onSelectFolder(props.itemId); + }; + const count = folderCounts[props.itemId] || 0; + + const handleIconButtonClick = (event: React.MouseEvent) => { + event.stopPropagation(); + handleDeleteRequest(props.itemId, count); + }; + + const handlePlusClick = (event: React.MouseEvent) => { + event.stopPropagation(); + setCreatingSubfolderId(props.itemId); + setIsCreatingRoot(true); + }; + + return ( + handleDragOver(e, props.itemId)} + onDragLeave={handleDragLeave} + onDrop={(e) => handleDrop(e, props.itemId)} + className={ + props.itemId === dragTargetId + ? "bg-white/10 rounded-md ring-2 ring-white" + : "" + } + slots={{ + label: CustomLabel, + }} + slotProps={ + { + label: { + onClick: handleIconButtonClick, + onPlusClick: handlePlusClick, + status, + }, + content: { onClick: handleContentClick }, + } as TreeItemSlotProps + } + /> + ); + }); + return (
= ({ {isCreatingRoot && (
@@ -533,8 +402,17 @@ const Sidebar: React.FC = ({ placeholder="Folder Name..." value={newRootName} onChange={(e) => setNewRootName(e.target.value)} - onBlur={() => !newRootName.trim() && setIsCreatingRoot(false)} - onKeyDown={(e) => e.key === "Escape" && setIsCreatingRoot(false)} + onBlur={() => { + !newRootName.trim(); + setIsCreatingRoot(false); + setCreatingSubfolderId(""); + }} + onKeyDown={(e) => { + if (e.key === "Escape") { + setIsCreatingRoot(false); + setCreatingSubfolderId(""); + } + }} />
@@ -563,30 +441,14 @@ const Sidebar: React.FC = ({
- {rootFolders.map((folder) => ( - - ))} + onRenameFolder(itemId, label)} + />
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 54b527a..701fd67 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/material": "^7.3.7", + "@mui/x-tree-view": "^8.25.0", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.4.0", "jszip": "^3.10.1", @@ -289,6 +290,28 @@ "node": ">=6.9.0" } }, + "node_modules/@base-ui/utils": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.2.3.tgz", + "integrity": "sha512-/CguQ2PDaOzeVOkllQR8nocJ0FFIDqsWIcURsVmm53QGo8NhFNpePjNlyPIB41luxfOqnG7PU0xicMEw3ls7XQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@floating-ui/utils": "^0.2.10", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@dimforge/rapier3d-compat": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", @@ -862,6 +885,12 @@ "node": ">=18" } }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1126,6 +1155,67 @@ } } }, + "node_modules/@mui/x-internals": { + "version": "8.25.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.25.0.tgz", + "integrity": "sha512-RKexkVaK3xvAeLBNeLAw6oJCsQrXkx7TYSRoSUmmJveydqOqoBbimv+nbc8PmL4UL0ShVNkaFL1YWY7kYCCXUA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.5", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@mui/x-tree-view": { + "version": "8.25.0", + "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-8.25.0.tgz", + "integrity": "sha512-0QBpK4d443dFrCIiR3QyNPFwh3vuYnaSIvIPGE3hUX+K7O+WSw7b62jcTOUJ5gKmdbKPef77dqC7AQj4JLNFkQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@base-ui/utils": "^0.2.3", + "@mui/utils": "^7.3.5", + "@mui/x-internals": "8.25.0", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -2628,6 +2718,12 @@ "node": ">=0.10.0" } }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -3347,6 +3443,17 @@ "@babel/helper-validator-identifier": "^7.28.5" } }, + "@base-ui/utils": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.2.3.tgz", + "integrity": "sha512-/CguQ2PDaOzeVOkllQR8nocJ0FFIDqsWIcURsVmm53QGo8NhFNpePjNlyPIB41luxfOqnG7PU0xicMEw3ls7XQ==", + "requires": { + "@babel/runtime": "^7.28.4", + "@floating-ui/utils": "^0.2.10", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + } + }, "@dimforge/rapier3d-compat": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", @@ -3655,6 +3762,11 @@ "dev": true, "optional": true }, + "@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" + }, "@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -3789,6 +3901,32 @@ "react-is": "^19.2.3" } }, + "@mui/x-internals": { + "version": "8.25.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.25.0.tgz", + "integrity": "sha512-RKexkVaK3xvAeLBNeLAw6oJCsQrXkx7TYSRoSUmmJveydqOqoBbimv+nbc8PmL4UL0ShVNkaFL1YWY7kYCCXUA==", + "requires": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.5", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + } + }, + "@mui/x-tree-view": { + "version": "8.25.0", + "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-8.25.0.tgz", + "integrity": "sha512-0QBpK4d443dFrCIiR3QyNPFwh3vuYnaSIvIPGE3hUX+K7O+WSw7b62jcTOUJ5gKmdbKPef77dqC7AQj4JLNFkQ==", + "requires": { + "@babel/runtime": "^7.28.4", + "@base-ui/utils": "^0.2.3", + "@mui/utils": "^7.3.5", + "@mui/x-internals": "8.25.0", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + } + }, "@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -4794,6 +4932,11 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, + "reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, "resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", diff --git a/frontend/package.json b/frontend/package.json index 236cbc0..3bf976f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/material": "^7.3.7", + "@mui/x-tree-view": "^8.25.0", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.4.0", "jszip": "^3.10.1", From a9e607057f125f846a79ea29d07c06bc1f8fa65a Mon Sep 17 00:00:00 2001 From: moddroid94 Date: Thu, 15 Jan 2026 16:59:53 +0100 Subject: [PATCH 06/24] migrated header and folder form to Material UI --- frontend/components/Sidebar.tsx | 111 ++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/frontend/components/Sidebar.tsx b/frontend/components/Sidebar.tsx index 967c12a..f63feb0 100644 --- a/frontend/components/Sidebar.tsx +++ b/frontend/components/Sidebar.tsx @@ -30,6 +30,9 @@ import { TreeItemProps, TreeItemSlotProps, } from "@mui/x-tree-view/TreeItem"; +import Container from "@mui/material/Container"; +import Button from "@mui/material/Button"; +import OutlinedInput from "@mui/material/OutlinedInput"; const APP_TAG = import.meta.env.VITE_APP_TAG || __APP_VERSION__ || "dev"; @@ -333,6 +336,7 @@ const Sidebar: React.FC = ({ event.stopPropagation(); setCreatingSubfolderId(props.itemId); setIsCreatingRoot(true); + document.getElementById("folder-name-input").focus(); }; return ( @@ -365,58 +369,81 @@ const Sidebar: React.FC = ({ }); return ( -
setDragTargetId(null)} >
-
- -
-

- STL Vault v{APP_TAG} -

+ +
+ +
+ + STLVault + + + v{APP_TAG} + +
- + New Root Folder +
- {isCreatingRoot && ( -
-
- setNewRootName(e.target.value)} - onBlur={() => { - !newRootName.trim(); + +
+ setNewRootName(e.target.value)} + onBlur={() => { + !newRootName.trim(); + setIsCreatingRoot(false); + setCreatingSubfolderId(""); + }} + onKeyDown={(e) => { + if (e.key === "Escape") { setIsCreatingRoot(false); setCreatingSubfolderId(""); - }} - onKeyDown={(e) => { - if (e.key === "Escape") { - setIsCreatingRoot(false); - setCreatingSubfolderId(""); - } - }} - /> -
- - )} + } + }} + /> +
+
-
+
+ ); }; From 58f902baded386cfd9dd40456bab8b3d21d7dc67 Mon Sep 17 00:00:00 2001 From: moddroid94 Date: Sat, 17 Jan 2026 01:44:40 +0100 Subject: [PATCH 07/24] completed migration of sidebar to Material UI added custom scrollbar via globals.css implemented back button for future navigation feature --- frontend/assets/globals.css | 26 ++++++ frontend/components/ModelList.tsx | 8 +- frontend/components/Sidebar.tsx | 136 +++++++++++++++--------------- frontend/index.html | 4 + frontend/vite.config.ts | 24 +++--- 5 files changed, 118 insertions(+), 80 deletions(-) create mode 100644 frontend/assets/globals.css diff --git a/frontend/assets/globals.css b/frontend/assets/globals.css new file mode 100644 index 0000000..dbbf6fb --- /dev/null +++ b/frontend/assets/globals.css @@ -0,0 +1,26 @@ +/* global.css */ + +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* width */ +::-webkit-scrollbar { + width: 10px; +} + +/* Track */ +::-webkit-scrollbar-track { + background: #000000; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background: #888; + border-radius: 5px; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + background: #555; +} diff --git a/frontend/components/ModelList.tsx b/frontend/components/ModelList.tsx index 8832dfb..f7547fc 100644 --- a/frontend/components/ModelList.tsx +++ b/frontend/components/ModelList.tsx @@ -12,6 +12,7 @@ import { DownloadIcon, ScreenShareIcon, XCircle, + ChevronLeft, } from "lucide-react"; import { STLModel, Folder } from "../types"; import { api } from "../services/api"; @@ -247,7 +248,7 @@ const ModelList: React.FC = ({ return (
{/* Header Section */} -
+
= ({
) : (
+ {/* Folders */} -
+
{/* Render Folders First */} {processedFolders.map((folder) => (
= ({ setDragTargetId(null)} > @@ -402,72 +404,69 @@ const Sidebar: React.FC = ({
-
- -
- -
-
- setNewRootName(e.target.value)} - onBlur={() => { - !newRootName.trim(); - setIsCreatingRoot(false); - setCreatingSubfolderId(""); +
+ -
- + Settings + -
-

+

+

Storage Used

-
+
+
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index ee75069..5ddb6a2 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,12 +1,14 @@ -import path from 'path'; -import fs from 'fs'; -import { defineConfig, loadEnv } from 'vite'; -import react from '@vitejs/plugin-react'; +import path from "path"; +import fs from "fs"; +import { defineConfig, loadEnv } from "vite"; +import react from "@vitejs/plugin-react"; export default defineConfig(({ mode }) => { - const env = loadEnv(mode, '.', ''); - const pkgJson = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf-8')); - const appVersion = env.VITE_APP_TAG || pkgJson.version || 'dev'; + const env = loadEnv(mode, ".", ""); + const pkgJson = JSON.parse( + fs.readFileSync(new URL("./package.json", import.meta.url), "utf-8") + ); + const appVersion = env.VITE_APP_TAG || pkgJson.version || "dev"; return { base: "/", preview: { @@ -14,7 +16,7 @@ export default defineConfig(({ mode }) => { }, server: { port: 5173, - host: '0.0.0.0', + host: "0.0.0.0", }, plugins: [react()], define: { @@ -22,8 +24,8 @@ export default defineConfig(({ mode }) => { }, resolve: { alias: { - '@': path.resolve(__dirname, '.'), - } - } + "@": path.resolve(__dirname, "."), + }, + }, }; }); From a630242910f05cfb08faa424e5871b3b0fd2be66 Mon Sep 17 00:00:00 2001 From: 0x4B1D Date: Sat, 17 Jan 2026 01:47:36 +0100 Subject: [PATCH 08/24] Updated dashboard preview to new MUI interface Updated the dashboard preview image in the README. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b69775..c6de9de 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ [![Docker Frontend CI](https://img.shields.io/github/actions/workflow/status/moddroid94/STLVault/Docker%20Backend%20CI.yml?style=for-the-badge&logo=docker&label=Backend)](https://github.com/moddroid94/STLVault/actions/workflows/Docker%20Backend%20CI.yml) [![Docker Pulls](https://img.shields.io/docker/pulls/moddroid94/stlvault-frontend?style=for-the-badge&logo=docker)](https://hub.docker.com/u/moddroid94) + ![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge) **STLVault** is a containerized 3D Model library manager and organizer, designed specifically for 3D printing enthusiasts. It provides a clean, modern web interface to manage your growing collection of STL, STEP, and 3MF files. @@ -43,7 +44,7 @@ ## πŸ“Έ Screenshots -![Dashboard Preview](https://github.com/user-attachments/assets/3d8aa851-392c-4bd0-8819-2a802ec63e2c) +![Dashboard Preview](https://github.com/user-attachments/assets/33be62e6-d7fd-455b-9ef1-e1d363bff6f8) ![Setting Page](https://github.com/user-attachments/assets/f1326a8c-3ef0-4b17-be5a-e75aea2cb59a) ![Upload Modal Preview](https://github.com/user-attachments/assets/34f995d3-bc09-489f-92f3-1408bf0196a0) ![Model Viewer/Info Preview](https://github.com/user-attachments/assets/ac373cf5-3952-4336-8b56-e2864127c3aa) From 6ecafd02729848d930c73cefdb9abe0274923287 Mon Sep 17 00:00:00 2001 From: moddroid94 Date: Sat, 17 Jan 2026 04:38:46 +0100 Subject: [PATCH 09/24] change sidebar icons color to secondary changed setting background to match theme --- frontend/components/Settings.tsx | 2 +- frontend/components/Sidebar.tsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/components/Settings.tsx b/frontend/components/Settings.tsx index e16856c..c9d3e3f 100644 --- a/frontend/components/Settings.tsx +++ b/frontend/components/Settings.tsx @@ -65,7 +65,7 @@ const Settings: React.FC = ({ onBack }) => { }; return ( -
+
{/* Header Section */}
diff --git a/frontend/components/Sidebar.tsx b/frontend/components/Sidebar.tsx index 1aaf9c7..c9ba9c1 100644 --- a/frontend/components/Sidebar.tsx +++ b/frontend/components/Sidebar.tsx @@ -297,6 +297,7 @@ const Sidebar: React.FC = ({ onClick={onPlusClick} aria-label="select item" size="small" + sx={{ color: 'grey.300' }} > @@ -305,6 +306,7 @@ const Sidebar: React.FC = ({ aria-label="select item" size="small" edge="end" + sx={{ color: 'grey.300' }} > From 792dcfc336da2f92af3e60c3f32aa702e16338f2 Mon Sep 17 00:00:00 2001 From: moddroid94 Date: Sat, 17 Jan 2026 04:39:09 +0100 Subject: [PATCH 10/24] migrated detail panel to Material UI changed 3D viewer background to black --- frontend/components/DetailPanel.tsx | 420 ++++++++++++++++------------ frontend/components/Viewer3D.tsx | 2 +- 2 files changed, 247 insertions(+), 175 deletions(-) diff --git a/frontend/components/DetailPanel.tsx b/frontend/components/DetailPanel.tsx index 611233d..359d0c6 100644 --- a/frontend/components/DetailPanel.tsx +++ b/frontend/components/DetailPanel.tsx @@ -14,10 +14,20 @@ import { FileUp, RefreshCw, AlertTriangle, + ScreenShareIcon, } from "lucide-react"; import { generateThumbnail } from "../services/thumbnailGenerator"; import { api } from "../services/api"; +import { Typography } from "@mui/material"; +import Button from "@mui/material/Button"; +import Stack from "@mui/material/Stack"; +import Divider from "@mui/material/Divider"; +import OutlinedInput from "@mui/material/OutlinedInput"; +import TextField from "@mui/material/TextField"; +import Badge from "@mui/material/Badge"; +import Chip from "@mui/material/Chip"; +import Grid from "@mui/material/Grid"; interface DetailPanelProps { model: STLModel | null; @@ -152,21 +162,18 @@ const DetailPanel: React.FC = ({ }; return ( -
+
{/* Header */}
-

Model Details

- + Model Details +
{/* Viewer */} -
+
= ({
{/* Actions */} - + + Open in Slicer + + + {/* Info Form */}
-
- - {!isEditing && ( - +
+ + Filename + + {isEditing ? ( + setEditName(e.target.value)} + /> + ) : ( + + {model.name} + + )} +
+ +
+ + Description + + + {isEditing ? ( + setEditDesc(e.target.value)} + placeholder="Add a description..." + multiline + /> + ) : ( + + {model.description || "No Description"} + )}
+ + + Metadata
{/* Quick Stats Grid */} -
+
+
+ + + Tags: + + {isEditing ? ( + setEditTags(e.target.value)} + placeholder="scifi, armor, character..." + multiline + /> + ) : ( + + {model.tags.length > 0 ? ( + model.tags.map((tag) => ( + + } + > + + )) + ) : ( + + No tags + + )} + + )} + +
+ +
-
+ - Added -
-

- {new Date(model.dateAdded).toLocaleDateString()} -

+ + Added: + + + {new Date(model.dateAdded).toLocaleDateString()} + +
-
+ - File Size -
-

- {(model.size / (1024 * 1024)).toFixed(2)} MB -

+ + File Size: + + + {(model.size / (1024 * 1024)).toFixed(2)} MB + +
+ {/* File Replacement Section (Edit Mode Only) */} {isEditing && (
- - Source File - -
- - + +
-

- Replaces geometry but keeps name/desc unless changed. -

- - - Thumbnail - -
-
+ + + Thumbnail: + + +
thumbnail
- - -
-

- Replaces Thumbnail. -

+ + +
)} -
- - Filename - - {isEditing ? ( - setEditName(e.target.value)} - /> - ) : ( -

{model.name}

- )} -
- -
- - Description - - {isEditing ? ( -