├── .github └── workflows │ └── build.yml ├── LICENSE.md ├── README.md ├── use_as_browser_package ├── .gitignore ├── README.md ├── index.html ├── models │ ├── DamagedHelmet.glb │ └── DamagedHelmet.license.md ├── package-lock.json ├── package.json └── start_server.bat ├── use_as_es6_module ├── .gitignore ├── README.md ├── index.html ├── models │ ├── DamagedHelmet.glb │ └── DamagedHelmet.license.md ├── package-lock.json ├── package.json ├── source │ └── main.js └── start_server.bat └── use_as_react_component ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── README.md ├── components ├── Basic3DViewer.js ├── ViewerWithUI.js └── ViewerWithUrls.js ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js └── index.js ├── postcss.config.js ├── public ├── bunny.stl ├── favicon.ico └── vercel.svg ├── styles ├── Home.module.css └── globals.css └── tailwind.config.js /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ${{ matrix.os-type }} 13 | 14 | strategy: 15 | matrix: 16 | node-version: [12.x, 14.x, 16.x] 17 | os-type: [windows-latest, macos-latest, ubuntu-latest] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm install 25 | working-directory: use_as_es6_module 26 | - run: npm run build 27 | working-directory: use_as_es6_module 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Viktor Kovacs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Online 3D Viewer Example 2 | 3 | [![Build](https://github.com/kovacsv/Online3DViewerExample/actions/workflows/build.yml/badge.svg)](https://github.com/kovacsv/Online3DViewerExample/actions/workflows/build.yml) 4 | 5 | [Online 3D Viewer](https://github.com/kovacsv/Online3DViewer) engine usage examples: 6 | - [Use as a browser-ready package](use_as_browser_package), 7 | - [Use as an ES6 module](use_as_es6_module). 8 | - [Use as a React component](use_as_react_component). 9 | -------------------------------------------------------------------------------- /use_as_browser_package/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | o3dv.zip 3 | o3dv 4 | -------------------------------------------------------------------------------- /use_as_browser_package/README.md: -------------------------------------------------------------------------------- 1 | # Online 3D Viewer Example 2 | 3 | Example of using [Online 3D Viewer](https://github.com/kovacsv/Online3DViewer) engine as a browser-ready package. 4 | 5 | ## How to use? 6 | 7 | Run the following commands from the root of the repository: 8 | ``` 9 | npm install 10 | npm start 11 | ``` 12 | 13 | Open http://localhost:8080 in your browser. 14 | -------------------------------------------------------------------------------- /use_as_browser_package/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Online 3D Viewer 9 | 10 | 11 | 19 | 20 | 28 | 29 | 30 | 31 |
35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /use_as_browser_package/models/DamagedHelmet.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovacsv/Online3DViewerExamples/02b487143b5e295ea91d26ef7bd956b1197aa690/use_as_browser_package/models/DamagedHelmet.glb -------------------------------------------------------------------------------- /use_as_browser_package/models/DamagedHelmet.license.md: -------------------------------------------------------------------------------- 1 | # Damaged Helmet 2 | 3 | ## Screenshot 4 | 5 | ![screenshot](screenshot/screenshot.png) 6 | 7 | ## License Information 8 | 9 | Battle Damaged Sci-fi Helmet - PBR by [theblueturtle_](https://sketchfab.com/theblueturtle_), published under a Creative Commons Attribution-NonCommercial license 10 | 11 | https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 12 | 13 | ## Modifications 14 | 15 | The original model was built on an early draft of glTF 2.0 that did not become final. This new model has been imported and re-exported from Blender to bring it into alignment with the final release glTF 2.0 specification. 16 | -------------------------------------------------------------------------------- /use_as_browser_package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "online-3d-viewer-example-browser", 3 | "description": "Online 3D Viewer Example", 4 | "version": "0.0.1", 5 | "repository": "github:kovacsv/Online3DViewerExample", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "npm run build && http-server", 9 | "build": "download https://github.com/kovacsv/Online3DViewer/releases/latest/download/o3dv.zip > o3dv.zip && 7z x -aos o3dv.zip -oo3dv" 10 | }, 11 | "devDependencies": { 12 | "download": "^8.0.0", 13 | "download-cli": "^1.1.1", 14 | "http-server": "^14.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /use_as_browser_package/start_server.bat: -------------------------------------------------------------------------------- 1 | npm start 2 | -------------------------------------------------------------------------------- /use_as_es6_module/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | node_modules 3 | -------------------------------------------------------------------------------- /use_as_es6_module/README.md: -------------------------------------------------------------------------------- 1 | # Online 3D Viewer Example 2 | 3 | Example of using [Online 3D Viewer](https://github.com/kovacsv/Online3DViewer) engine as an ES6 module. 4 | 5 | ## How to use? 6 | 7 | Run the following commands from the root of the repository: 8 | ``` 9 | npm install 10 | npm start 11 | ``` 12 | 13 | Open http://localhost:8080 in your browser. 14 | -------------------------------------------------------------------------------- /use_as_es6_module/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Online 3D Viewer 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 |
27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /use_as_es6_module/models/DamagedHelmet.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovacsv/Online3DViewerExamples/02b487143b5e295ea91d26ef7bd956b1197aa690/use_as_es6_module/models/DamagedHelmet.glb -------------------------------------------------------------------------------- /use_as_es6_module/models/DamagedHelmet.license.md: -------------------------------------------------------------------------------- 1 | # Damaged Helmet 2 | 3 | ## Screenshot 4 | 5 | ![screenshot](screenshot/screenshot.png) 6 | 7 | ## License Information 8 | 9 | Battle Damaged Sci-fi Helmet - PBR by [theblueturtle_](https://sketchfab.com/theblueturtle_), published under a Creative Commons Attribution-NonCommercial license 10 | 11 | https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 12 | 13 | ## Modifications 14 | 15 | The original model was built on an early draft of glTF 2.0 that did not become final. This new model has been imported and re-exported from Blender to bring it into alignment with the final release glTF 2.0 specification. 16 | -------------------------------------------------------------------------------- /use_as_es6_module/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "online-3d-viewer-example-esm", 3 | "version": "0.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "online-3d-viewer-example-esm", 9 | "version": "0.0.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "online-3d-viewer": "*" 13 | }, 14 | "devDependencies": { 15 | "copyfiles": "^2.4.1", 16 | "esbuild": "^0.14.23", 17 | "http-server": "^14.0.0" 18 | } 19 | }, 20 | "node_modules/@esbuild/linux-loong64": { 21 | "version": "0.14.54", 22 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", 23 | "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", 24 | "cpu": [ 25 | "loong64" 26 | ], 27 | "dev": true, 28 | "optional": true, 29 | "os": [ 30 | "linux" 31 | ], 32 | "engines": { 33 | "node": ">=12" 34 | } 35 | }, 36 | "node_modules/@simonwep/pickr": { 37 | "version": "1.8.2", 38 | "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz", 39 | "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==", 40 | "dependencies": { 41 | "core-js": "^3.15.1", 42 | "nanopop": "^2.1.0" 43 | } 44 | }, 45 | "node_modules/ansi-regex": { 46 | "version": "5.0.1", 47 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 48 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 49 | "dev": true, 50 | "engines": { 51 | "node": ">=8" 52 | } 53 | }, 54 | "node_modules/ansi-styles": { 55 | "version": "4.3.0", 56 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 57 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 58 | "dev": true, 59 | "dependencies": { 60 | "color-convert": "^2.0.1" 61 | }, 62 | "engines": { 63 | "node": ">=8" 64 | }, 65 | "funding": { 66 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 67 | } 68 | }, 69 | "node_modules/async": { 70 | "version": "2.6.4", 71 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", 72 | "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", 73 | "dev": true, 74 | "dependencies": { 75 | "lodash": "^4.17.14" 76 | } 77 | }, 78 | "node_modules/balanced-match": { 79 | "version": "1.0.2", 80 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 81 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 82 | "dev": true 83 | }, 84 | "node_modules/basic-auth": { 85 | "version": "2.0.1", 86 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 87 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 88 | "dev": true, 89 | "dependencies": { 90 | "safe-buffer": "5.1.2" 91 | }, 92 | "engines": { 93 | "node": ">= 0.8" 94 | } 95 | }, 96 | "node_modules/brace-expansion": { 97 | "version": "1.1.11", 98 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 99 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 100 | "dev": true, 101 | "dependencies": { 102 | "balanced-match": "^1.0.0", 103 | "concat-map": "0.0.1" 104 | } 105 | }, 106 | "node_modules/call-bind": { 107 | "version": "1.0.2", 108 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 109 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 110 | "dev": true, 111 | "dependencies": { 112 | "function-bind": "^1.1.1", 113 | "get-intrinsic": "^1.0.2" 114 | }, 115 | "funding": { 116 | "url": "https://github.com/sponsors/ljharb" 117 | } 118 | }, 119 | "node_modules/chalk": { 120 | "version": "4.1.2", 121 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 122 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 123 | "dev": true, 124 | "dependencies": { 125 | "ansi-styles": "^4.1.0", 126 | "supports-color": "^7.1.0" 127 | }, 128 | "engines": { 129 | "node": ">=10" 130 | }, 131 | "funding": { 132 | "url": "https://github.com/chalk/chalk?sponsor=1" 133 | } 134 | }, 135 | "node_modules/cliui": { 136 | "version": "7.0.4", 137 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 138 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 139 | "dev": true, 140 | "dependencies": { 141 | "string-width": "^4.2.0", 142 | "strip-ansi": "^6.0.0", 143 | "wrap-ansi": "^7.0.0" 144 | } 145 | }, 146 | "node_modules/color-convert": { 147 | "version": "2.0.1", 148 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 149 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 150 | "dev": true, 151 | "dependencies": { 152 | "color-name": "~1.1.4" 153 | }, 154 | "engines": { 155 | "node": ">=7.0.0" 156 | } 157 | }, 158 | "node_modules/color-name": { 159 | "version": "1.1.4", 160 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 161 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 162 | "dev": true 163 | }, 164 | "node_modules/concat-map": { 165 | "version": "0.0.1", 166 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 167 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 168 | "dev": true 169 | }, 170 | "node_modules/copyfiles": { 171 | "version": "2.4.1", 172 | "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", 173 | "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", 174 | "dev": true, 175 | "dependencies": { 176 | "glob": "^7.0.5", 177 | "minimatch": "^3.0.3", 178 | "mkdirp": "^1.0.4", 179 | "noms": "0.0.0", 180 | "through2": "^2.0.1", 181 | "untildify": "^4.0.0", 182 | "yargs": "^16.1.0" 183 | }, 184 | "bin": { 185 | "copyfiles": "copyfiles", 186 | "copyup": "copyfiles" 187 | } 188 | }, 189 | "node_modules/core-js": { 190 | "version": "3.25.2", 191 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.2.tgz", 192 | "integrity": "sha512-YB4IAT1bjEfxTJ1XYy11hJAKskO+qmhuDBM8/guIfMz4JvdsAQAqvyb97zXX7JgSrfPLG5mRGFWJwJD39ruq2A==", 193 | "hasInstallScript": true, 194 | "funding": { 195 | "type": "opencollective", 196 | "url": "https://opencollective.com/core-js" 197 | } 198 | }, 199 | "node_modules/core-util-is": { 200 | "version": "1.0.3", 201 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 202 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", 203 | "dev": true 204 | }, 205 | "node_modules/corser": { 206 | "version": "2.0.1", 207 | "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", 208 | "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", 209 | "dev": true, 210 | "engines": { 211 | "node": ">= 0.4.0" 212 | } 213 | }, 214 | "node_modules/debug": { 215 | "version": "3.2.7", 216 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 217 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 218 | "dev": true, 219 | "dependencies": { 220 | "ms": "^2.1.1" 221 | } 222 | }, 223 | "node_modules/draco3d": { 224 | "version": "1.5.3", 225 | "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.3.tgz", 226 | "integrity": "sha512-Ahum6SewAd1oVMm6Fk8T/zCE0qbzjohhO5pl1Xp5Outl4JKv7jYicfd5vNtkzImx94XE35fhNXVqHk9ajt+6Tg==" 227 | }, 228 | "node_modules/emoji-regex": { 229 | "version": "8.0.0", 230 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 231 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 232 | "dev": true 233 | }, 234 | "node_modules/esbuild": { 235 | "version": "0.14.54", 236 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", 237 | "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", 238 | "dev": true, 239 | "hasInstallScript": true, 240 | "bin": { 241 | "esbuild": "bin/esbuild" 242 | }, 243 | "engines": { 244 | "node": ">=12" 245 | }, 246 | "optionalDependencies": { 247 | "@esbuild/linux-loong64": "0.14.54", 248 | "esbuild-android-64": "0.14.54", 249 | "esbuild-android-arm64": "0.14.54", 250 | "esbuild-darwin-64": "0.14.54", 251 | "esbuild-darwin-arm64": "0.14.54", 252 | "esbuild-freebsd-64": "0.14.54", 253 | "esbuild-freebsd-arm64": "0.14.54", 254 | "esbuild-linux-32": "0.14.54", 255 | "esbuild-linux-64": "0.14.54", 256 | "esbuild-linux-arm": "0.14.54", 257 | "esbuild-linux-arm64": "0.14.54", 258 | "esbuild-linux-mips64le": "0.14.54", 259 | "esbuild-linux-ppc64le": "0.14.54", 260 | "esbuild-linux-riscv64": "0.14.54", 261 | "esbuild-linux-s390x": "0.14.54", 262 | "esbuild-netbsd-64": "0.14.54", 263 | "esbuild-openbsd-64": "0.14.54", 264 | "esbuild-sunos-64": "0.14.54", 265 | "esbuild-windows-32": "0.14.54", 266 | "esbuild-windows-64": "0.14.54", 267 | "esbuild-windows-arm64": "0.14.54" 268 | } 269 | }, 270 | "node_modules/esbuild-android-64": { 271 | "version": "0.14.54", 272 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", 273 | "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", 274 | "cpu": [ 275 | "x64" 276 | ], 277 | "dev": true, 278 | "optional": true, 279 | "os": [ 280 | "android" 281 | ], 282 | "engines": { 283 | "node": ">=12" 284 | } 285 | }, 286 | "node_modules/esbuild-android-arm64": { 287 | "version": "0.14.54", 288 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", 289 | "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", 290 | "cpu": [ 291 | "arm64" 292 | ], 293 | "dev": true, 294 | "optional": true, 295 | "os": [ 296 | "android" 297 | ], 298 | "engines": { 299 | "node": ">=12" 300 | } 301 | }, 302 | "node_modules/esbuild-darwin-64": { 303 | "version": "0.14.54", 304 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", 305 | "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", 306 | "cpu": [ 307 | "x64" 308 | ], 309 | "dev": true, 310 | "optional": true, 311 | "os": [ 312 | "darwin" 313 | ], 314 | "engines": { 315 | "node": ">=12" 316 | } 317 | }, 318 | "node_modules/esbuild-darwin-arm64": { 319 | "version": "0.14.54", 320 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", 321 | "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", 322 | "cpu": [ 323 | "arm64" 324 | ], 325 | "dev": true, 326 | "optional": true, 327 | "os": [ 328 | "darwin" 329 | ], 330 | "engines": { 331 | "node": ">=12" 332 | } 333 | }, 334 | "node_modules/esbuild-freebsd-64": { 335 | "version": "0.14.54", 336 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", 337 | "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", 338 | "cpu": [ 339 | "x64" 340 | ], 341 | "dev": true, 342 | "optional": true, 343 | "os": [ 344 | "freebsd" 345 | ], 346 | "engines": { 347 | "node": ">=12" 348 | } 349 | }, 350 | "node_modules/esbuild-freebsd-arm64": { 351 | "version": "0.14.54", 352 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", 353 | "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", 354 | "cpu": [ 355 | "arm64" 356 | ], 357 | "dev": true, 358 | "optional": true, 359 | "os": [ 360 | "freebsd" 361 | ], 362 | "engines": { 363 | "node": ">=12" 364 | } 365 | }, 366 | "node_modules/esbuild-linux-32": { 367 | "version": "0.14.54", 368 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", 369 | "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", 370 | "cpu": [ 371 | "ia32" 372 | ], 373 | "dev": true, 374 | "optional": true, 375 | "os": [ 376 | "linux" 377 | ], 378 | "engines": { 379 | "node": ">=12" 380 | } 381 | }, 382 | "node_modules/esbuild-linux-64": { 383 | "version": "0.14.54", 384 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", 385 | "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", 386 | "cpu": [ 387 | "x64" 388 | ], 389 | "dev": true, 390 | "optional": true, 391 | "os": [ 392 | "linux" 393 | ], 394 | "engines": { 395 | "node": ">=12" 396 | } 397 | }, 398 | "node_modules/esbuild-linux-arm": { 399 | "version": "0.14.54", 400 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", 401 | "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", 402 | "cpu": [ 403 | "arm" 404 | ], 405 | "dev": true, 406 | "optional": true, 407 | "os": [ 408 | "linux" 409 | ], 410 | "engines": { 411 | "node": ">=12" 412 | } 413 | }, 414 | "node_modules/esbuild-linux-arm64": { 415 | "version": "0.14.54", 416 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", 417 | "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", 418 | "cpu": [ 419 | "arm64" 420 | ], 421 | "dev": true, 422 | "optional": true, 423 | "os": [ 424 | "linux" 425 | ], 426 | "engines": { 427 | "node": ">=12" 428 | } 429 | }, 430 | "node_modules/esbuild-linux-mips64le": { 431 | "version": "0.14.54", 432 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", 433 | "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", 434 | "cpu": [ 435 | "mips64el" 436 | ], 437 | "dev": true, 438 | "optional": true, 439 | "os": [ 440 | "linux" 441 | ], 442 | "engines": { 443 | "node": ">=12" 444 | } 445 | }, 446 | "node_modules/esbuild-linux-ppc64le": { 447 | "version": "0.14.54", 448 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", 449 | "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", 450 | "cpu": [ 451 | "ppc64" 452 | ], 453 | "dev": true, 454 | "optional": true, 455 | "os": [ 456 | "linux" 457 | ], 458 | "engines": { 459 | "node": ">=12" 460 | } 461 | }, 462 | "node_modules/esbuild-linux-riscv64": { 463 | "version": "0.14.54", 464 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", 465 | "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", 466 | "cpu": [ 467 | "riscv64" 468 | ], 469 | "dev": true, 470 | "optional": true, 471 | "os": [ 472 | "linux" 473 | ], 474 | "engines": { 475 | "node": ">=12" 476 | } 477 | }, 478 | "node_modules/esbuild-linux-s390x": { 479 | "version": "0.14.54", 480 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", 481 | "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", 482 | "cpu": [ 483 | "s390x" 484 | ], 485 | "dev": true, 486 | "optional": true, 487 | "os": [ 488 | "linux" 489 | ], 490 | "engines": { 491 | "node": ">=12" 492 | } 493 | }, 494 | "node_modules/esbuild-netbsd-64": { 495 | "version": "0.14.54", 496 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", 497 | "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", 498 | "cpu": [ 499 | "x64" 500 | ], 501 | "dev": true, 502 | "optional": true, 503 | "os": [ 504 | "netbsd" 505 | ], 506 | "engines": { 507 | "node": ">=12" 508 | } 509 | }, 510 | "node_modules/esbuild-openbsd-64": { 511 | "version": "0.14.54", 512 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", 513 | "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", 514 | "cpu": [ 515 | "x64" 516 | ], 517 | "dev": true, 518 | "optional": true, 519 | "os": [ 520 | "openbsd" 521 | ], 522 | "engines": { 523 | "node": ">=12" 524 | } 525 | }, 526 | "node_modules/esbuild-sunos-64": { 527 | "version": "0.14.54", 528 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", 529 | "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", 530 | "cpu": [ 531 | "x64" 532 | ], 533 | "dev": true, 534 | "optional": true, 535 | "os": [ 536 | "sunos" 537 | ], 538 | "engines": { 539 | "node": ">=12" 540 | } 541 | }, 542 | "node_modules/esbuild-windows-32": { 543 | "version": "0.14.54", 544 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", 545 | "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", 546 | "cpu": [ 547 | "ia32" 548 | ], 549 | "dev": true, 550 | "optional": true, 551 | "os": [ 552 | "win32" 553 | ], 554 | "engines": { 555 | "node": ">=12" 556 | } 557 | }, 558 | "node_modules/esbuild-windows-64": { 559 | "version": "0.14.54", 560 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", 561 | "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", 562 | "cpu": [ 563 | "x64" 564 | ], 565 | "dev": true, 566 | "optional": true, 567 | "os": [ 568 | "win32" 569 | ], 570 | "engines": { 571 | "node": ">=12" 572 | } 573 | }, 574 | "node_modules/esbuild-windows-arm64": { 575 | "version": "0.14.54", 576 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", 577 | "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", 578 | "cpu": [ 579 | "arm64" 580 | ], 581 | "dev": true, 582 | "optional": true, 583 | "os": [ 584 | "win32" 585 | ], 586 | "engines": { 587 | "node": ">=12" 588 | } 589 | }, 590 | "node_modules/escalade": { 591 | "version": "3.1.1", 592 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 593 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 594 | "dev": true, 595 | "engines": { 596 | "node": ">=6" 597 | } 598 | }, 599 | "node_modules/eventemitter3": { 600 | "version": "4.0.7", 601 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", 602 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", 603 | "dev": true 604 | }, 605 | "node_modules/fflate": { 606 | "version": "0.7.3", 607 | "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.3.tgz", 608 | "integrity": "sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==" 609 | }, 610 | "node_modules/follow-redirects": { 611 | "version": "1.15.2", 612 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 613 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 614 | "dev": true, 615 | "funding": [ 616 | { 617 | "type": "individual", 618 | "url": "https://github.com/sponsors/RubenVerborgh" 619 | } 620 | ], 621 | "engines": { 622 | "node": ">=4.0" 623 | }, 624 | "peerDependenciesMeta": { 625 | "debug": { 626 | "optional": true 627 | } 628 | } 629 | }, 630 | "node_modules/fs.realpath": { 631 | "version": "1.0.0", 632 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 633 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 634 | "dev": true 635 | }, 636 | "node_modules/function-bind": { 637 | "version": "1.1.1", 638 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 639 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 640 | "dev": true 641 | }, 642 | "node_modules/get-caller-file": { 643 | "version": "2.0.5", 644 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 645 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 646 | "dev": true, 647 | "engines": { 648 | "node": "6.* || 8.* || >= 10.*" 649 | } 650 | }, 651 | "node_modules/get-intrinsic": { 652 | "version": "1.1.3", 653 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 654 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 655 | "dev": true, 656 | "dependencies": { 657 | "function-bind": "^1.1.1", 658 | "has": "^1.0.3", 659 | "has-symbols": "^1.0.3" 660 | }, 661 | "funding": { 662 | "url": "https://github.com/sponsors/ljharb" 663 | } 664 | }, 665 | "node_modules/glob": { 666 | "version": "7.2.3", 667 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 668 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 669 | "dev": true, 670 | "dependencies": { 671 | "fs.realpath": "^1.0.0", 672 | "inflight": "^1.0.4", 673 | "inherits": "2", 674 | "minimatch": "^3.1.1", 675 | "once": "^1.3.0", 676 | "path-is-absolute": "^1.0.0" 677 | }, 678 | "engines": { 679 | "node": "*" 680 | }, 681 | "funding": { 682 | "url": "https://github.com/sponsors/isaacs" 683 | } 684 | }, 685 | "node_modules/has": { 686 | "version": "1.0.3", 687 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 688 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 689 | "dev": true, 690 | "dependencies": { 691 | "function-bind": "^1.1.1" 692 | }, 693 | "engines": { 694 | "node": ">= 0.4.0" 695 | } 696 | }, 697 | "node_modules/has-flag": { 698 | "version": "4.0.0", 699 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 700 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 701 | "dev": true, 702 | "engines": { 703 | "node": ">=8" 704 | } 705 | }, 706 | "node_modules/has-symbols": { 707 | "version": "1.0.3", 708 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 709 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 710 | "dev": true, 711 | "engines": { 712 | "node": ">= 0.4" 713 | }, 714 | "funding": { 715 | "url": "https://github.com/sponsors/ljharb" 716 | } 717 | }, 718 | "node_modules/he": { 719 | "version": "1.2.0", 720 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 721 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 722 | "dev": true, 723 | "bin": { 724 | "he": "bin/he" 725 | } 726 | }, 727 | "node_modules/html-encoding-sniffer": { 728 | "version": "3.0.0", 729 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", 730 | "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", 731 | "dev": true, 732 | "dependencies": { 733 | "whatwg-encoding": "^2.0.0" 734 | }, 735 | "engines": { 736 | "node": ">=12" 737 | } 738 | }, 739 | "node_modules/http-proxy": { 740 | "version": "1.18.1", 741 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", 742 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", 743 | "dev": true, 744 | "dependencies": { 745 | "eventemitter3": "^4.0.0", 746 | "follow-redirects": "^1.0.0", 747 | "requires-port": "^1.0.0" 748 | }, 749 | "engines": { 750 | "node": ">=8.0.0" 751 | } 752 | }, 753 | "node_modules/http-server": { 754 | "version": "14.1.1", 755 | "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", 756 | "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", 757 | "dev": true, 758 | "dependencies": { 759 | "basic-auth": "^2.0.1", 760 | "chalk": "^4.1.2", 761 | "corser": "^2.0.1", 762 | "he": "^1.2.0", 763 | "html-encoding-sniffer": "^3.0.0", 764 | "http-proxy": "^1.18.1", 765 | "mime": "^1.6.0", 766 | "minimist": "^1.2.6", 767 | "opener": "^1.5.1", 768 | "portfinder": "^1.0.28", 769 | "secure-compare": "3.0.1", 770 | "union": "~0.5.0", 771 | "url-join": "^4.0.1" 772 | }, 773 | "bin": { 774 | "http-server": "bin/http-server" 775 | }, 776 | "engines": { 777 | "node": ">=12" 778 | } 779 | }, 780 | "node_modules/iconv-lite": { 781 | "version": "0.6.3", 782 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 783 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 784 | "dev": true, 785 | "dependencies": { 786 | "safer-buffer": ">= 2.1.2 < 3.0.0" 787 | }, 788 | "engines": { 789 | "node": ">=0.10.0" 790 | } 791 | }, 792 | "node_modules/inflight": { 793 | "version": "1.0.6", 794 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 795 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 796 | "dev": true, 797 | "dependencies": { 798 | "once": "^1.3.0", 799 | "wrappy": "1" 800 | } 801 | }, 802 | "node_modules/inherits": { 803 | "version": "2.0.4", 804 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 805 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 806 | "dev": true 807 | }, 808 | "node_modules/is-fullwidth-code-point": { 809 | "version": "3.0.0", 810 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 811 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 812 | "dev": true, 813 | "engines": { 814 | "node": ">=8" 815 | } 816 | }, 817 | "node_modules/isarray": { 818 | "version": "0.0.1", 819 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 820 | "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", 821 | "dev": true 822 | }, 823 | "node_modules/lodash": { 824 | "version": "4.17.21", 825 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 826 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 827 | "dev": true 828 | }, 829 | "node_modules/mime": { 830 | "version": "1.6.0", 831 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 832 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 833 | "dev": true, 834 | "bin": { 835 | "mime": "cli.js" 836 | }, 837 | "engines": { 838 | "node": ">=4" 839 | } 840 | }, 841 | "node_modules/minimatch": { 842 | "version": "3.1.2", 843 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 844 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 845 | "dev": true, 846 | "dependencies": { 847 | "brace-expansion": "^1.1.7" 848 | }, 849 | "engines": { 850 | "node": "*" 851 | } 852 | }, 853 | "node_modules/minimist": { 854 | "version": "1.2.6", 855 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 856 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", 857 | "dev": true 858 | }, 859 | "node_modules/mkdirp": { 860 | "version": "1.0.4", 861 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 862 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 863 | "dev": true, 864 | "bin": { 865 | "mkdirp": "bin/cmd.js" 866 | }, 867 | "engines": { 868 | "node": ">=10" 869 | } 870 | }, 871 | "node_modules/ms": { 872 | "version": "2.1.3", 873 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 874 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 875 | "dev": true 876 | }, 877 | "node_modules/nanopop": { 878 | "version": "2.2.0", 879 | "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.2.0.tgz", 880 | "integrity": "sha512-E9JaHcxh3ere8/BEZHAcnuD10RluTSPyTToBvoFWS9/7DcCx6gyKjbn7M7Bx7E1veCxCuY1iO6h4+gdAf1j73Q==" 881 | }, 882 | "node_modules/noms": { 883 | "version": "0.0.0", 884 | "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", 885 | "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", 886 | "dev": true, 887 | "dependencies": { 888 | "inherits": "^2.0.1", 889 | "readable-stream": "~1.0.31" 890 | } 891 | }, 892 | "node_modules/object-inspect": { 893 | "version": "1.12.2", 894 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 895 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", 896 | "dev": true, 897 | "funding": { 898 | "url": "https://github.com/sponsors/ljharb" 899 | } 900 | }, 901 | "node_modules/occt-import-js": { 902 | "version": "0.0.10", 903 | "resolved": "https://registry.npmjs.org/occt-import-js/-/occt-import-js-0.0.10.tgz", 904 | "integrity": "sha512-DvFitPZswkOqkkM7YvuIlykHp30e3Da1f83hVdVQ0WegcwNe933RF2/iQ0AnpO/T0SNnfjXSA0d68My8XWIr+Q==" 905 | }, 906 | "node_modules/once": { 907 | "version": "1.4.0", 908 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 909 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 910 | "dev": true, 911 | "dependencies": { 912 | "wrappy": "1" 913 | } 914 | }, 915 | "node_modules/online-3d-viewer": { 916 | "version": "0.8.16", 917 | "resolved": "https://registry.npmjs.org/online-3d-viewer/-/online-3d-viewer-0.8.16.tgz", 918 | "integrity": "sha512-CKSMNzRuJytz818ZpOg0s8eolhS+6z8ams2jFBJ8uWzky+uglZGoBj0r97wTIz26jijb5nSY+hnV9o+KZsnV8w==", 919 | "dependencies": { 920 | "@simonwep/pickr": "1.8.2", 921 | "draco3d": "1.5.3", 922 | "fflate": "0.7.3", 923 | "occt-import-js": "0.0.10", 924 | "rhino3dm": "7.15.0", 925 | "three": "0.144.0", 926 | "web-ifc": "0.0.35" 927 | } 928 | }, 929 | "node_modules/opener": { 930 | "version": "1.5.2", 931 | "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", 932 | "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", 933 | "dev": true, 934 | "bin": { 935 | "opener": "bin/opener-bin.js" 936 | } 937 | }, 938 | "node_modules/path-is-absolute": { 939 | "version": "1.0.1", 940 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 941 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 942 | "dev": true, 943 | "engines": { 944 | "node": ">=0.10.0" 945 | } 946 | }, 947 | "node_modules/portfinder": { 948 | "version": "1.0.32", 949 | "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", 950 | "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", 951 | "dev": true, 952 | "dependencies": { 953 | "async": "^2.6.4", 954 | "debug": "^3.2.7", 955 | "mkdirp": "^0.5.6" 956 | }, 957 | "engines": { 958 | "node": ">= 0.12.0" 959 | } 960 | }, 961 | "node_modules/portfinder/node_modules/mkdirp": { 962 | "version": "0.5.6", 963 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 964 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 965 | "dev": true, 966 | "dependencies": { 967 | "minimist": "^1.2.6" 968 | }, 969 | "bin": { 970 | "mkdirp": "bin/cmd.js" 971 | } 972 | }, 973 | "node_modules/process-nextick-args": { 974 | "version": "2.0.1", 975 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 976 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 977 | "dev": true 978 | }, 979 | "node_modules/qs": { 980 | "version": "6.11.0", 981 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 982 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 983 | "dev": true, 984 | "dependencies": { 985 | "side-channel": "^1.0.4" 986 | }, 987 | "engines": { 988 | "node": ">=0.6" 989 | }, 990 | "funding": { 991 | "url": "https://github.com/sponsors/ljharb" 992 | } 993 | }, 994 | "node_modules/readable-stream": { 995 | "version": "1.0.34", 996 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 997 | "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", 998 | "dev": true, 999 | "dependencies": { 1000 | "core-util-is": "~1.0.0", 1001 | "inherits": "~2.0.1", 1002 | "isarray": "0.0.1", 1003 | "string_decoder": "~0.10.x" 1004 | } 1005 | }, 1006 | "node_modules/require-directory": { 1007 | "version": "2.1.1", 1008 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1009 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1010 | "dev": true, 1011 | "engines": { 1012 | "node": ">=0.10.0" 1013 | } 1014 | }, 1015 | "node_modules/requires-port": { 1016 | "version": "1.0.0", 1017 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 1018 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", 1019 | "dev": true 1020 | }, 1021 | "node_modules/rhino3dm": { 1022 | "version": "7.15.0", 1023 | "resolved": "https://registry.npmjs.org/rhino3dm/-/rhino3dm-7.15.0.tgz", 1024 | "integrity": "sha512-EOYK79KqgWNvonzXRH3eSO5Qmz1aBUzFvEZad2JUMTGq65lNOOU/LoUgFCJ0P/EZNdMJD+pNTdn9zgANOautgw==" 1025 | }, 1026 | "node_modules/safe-buffer": { 1027 | "version": "5.1.2", 1028 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1029 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1030 | "dev": true 1031 | }, 1032 | "node_modules/safer-buffer": { 1033 | "version": "2.1.2", 1034 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1035 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1036 | "dev": true 1037 | }, 1038 | "node_modules/secure-compare": { 1039 | "version": "3.0.1", 1040 | "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", 1041 | "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", 1042 | "dev": true 1043 | }, 1044 | "node_modules/side-channel": { 1045 | "version": "1.0.4", 1046 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1047 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1048 | "dev": true, 1049 | "dependencies": { 1050 | "call-bind": "^1.0.0", 1051 | "get-intrinsic": "^1.0.2", 1052 | "object-inspect": "^1.9.0" 1053 | }, 1054 | "funding": { 1055 | "url": "https://github.com/sponsors/ljharb" 1056 | } 1057 | }, 1058 | "node_modules/string_decoder": { 1059 | "version": "0.10.31", 1060 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1061 | "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", 1062 | "dev": true 1063 | }, 1064 | "node_modules/string-width": { 1065 | "version": "4.2.3", 1066 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1067 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1068 | "dev": true, 1069 | "dependencies": { 1070 | "emoji-regex": "^8.0.0", 1071 | "is-fullwidth-code-point": "^3.0.0", 1072 | "strip-ansi": "^6.0.1" 1073 | }, 1074 | "engines": { 1075 | "node": ">=8" 1076 | } 1077 | }, 1078 | "node_modules/strip-ansi": { 1079 | "version": "6.0.1", 1080 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1081 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1082 | "dev": true, 1083 | "dependencies": { 1084 | "ansi-regex": "^5.0.1" 1085 | }, 1086 | "engines": { 1087 | "node": ">=8" 1088 | } 1089 | }, 1090 | "node_modules/supports-color": { 1091 | "version": "7.2.0", 1092 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1093 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1094 | "dev": true, 1095 | "dependencies": { 1096 | "has-flag": "^4.0.0" 1097 | }, 1098 | "engines": { 1099 | "node": ">=8" 1100 | } 1101 | }, 1102 | "node_modules/three": { 1103 | "version": "0.144.0", 1104 | "resolved": "https://registry.npmjs.org/three/-/three-0.144.0.tgz", 1105 | "integrity": "sha512-R8AXPuqfjfRJKkYoTQcTK7A6i3AdO9++2n8ubya/GTU+fEHhYKu1ZooRSCPkx69jbnzT7dD/xEo6eROQTt2lJw==" 1106 | }, 1107 | "node_modules/through2": { 1108 | "version": "2.0.5", 1109 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", 1110 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", 1111 | "dev": true, 1112 | "dependencies": { 1113 | "readable-stream": "~2.3.6", 1114 | "xtend": "~4.0.1" 1115 | } 1116 | }, 1117 | "node_modules/through2/node_modules/isarray": { 1118 | "version": "1.0.0", 1119 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1120 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", 1121 | "dev": true 1122 | }, 1123 | "node_modules/through2/node_modules/readable-stream": { 1124 | "version": "2.3.7", 1125 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 1126 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 1127 | "dev": true, 1128 | "dependencies": { 1129 | "core-util-is": "~1.0.0", 1130 | "inherits": "~2.0.3", 1131 | "isarray": "~1.0.0", 1132 | "process-nextick-args": "~2.0.0", 1133 | "safe-buffer": "~5.1.1", 1134 | "string_decoder": "~1.1.1", 1135 | "util-deprecate": "~1.0.1" 1136 | } 1137 | }, 1138 | "node_modules/through2/node_modules/string_decoder": { 1139 | "version": "1.1.1", 1140 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1141 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1142 | "dev": true, 1143 | "dependencies": { 1144 | "safe-buffer": "~5.1.0" 1145 | } 1146 | }, 1147 | "node_modules/union": { 1148 | "version": "0.5.0", 1149 | "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", 1150 | "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", 1151 | "dev": true, 1152 | "dependencies": { 1153 | "qs": "^6.4.0" 1154 | }, 1155 | "engines": { 1156 | "node": ">= 0.8.0" 1157 | } 1158 | }, 1159 | "node_modules/untildify": { 1160 | "version": "4.0.0", 1161 | "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", 1162 | "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", 1163 | "dev": true, 1164 | "engines": { 1165 | "node": ">=8" 1166 | } 1167 | }, 1168 | "node_modules/url-join": { 1169 | "version": "4.0.1", 1170 | "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", 1171 | "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", 1172 | "dev": true 1173 | }, 1174 | "node_modules/util-deprecate": { 1175 | "version": "1.0.2", 1176 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1177 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 1178 | "dev": true 1179 | }, 1180 | "node_modules/web-ifc": { 1181 | "version": "0.0.35", 1182 | "resolved": "https://registry.npmjs.org/web-ifc/-/web-ifc-0.0.35.tgz", 1183 | "integrity": "sha512-if/L9RiJAD+jJ1oWtHj44+o2NG6K6azK0otE9Pbppr5hNBnouYfJQNmnHZ+Ya+wZeaByo9KeU64D60Y6lFnT4w==" 1184 | }, 1185 | "node_modules/whatwg-encoding": { 1186 | "version": "2.0.0", 1187 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", 1188 | "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", 1189 | "dev": true, 1190 | "dependencies": { 1191 | "iconv-lite": "0.6.3" 1192 | }, 1193 | "engines": { 1194 | "node": ">=12" 1195 | } 1196 | }, 1197 | "node_modules/wrap-ansi": { 1198 | "version": "7.0.0", 1199 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1200 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1201 | "dev": true, 1202 | "dependencies": { 1203 | "ansi-styles": "^4.0.0", 1204 | "string-width": "^4.1.0", 1205 | "strip-ansi": "^6.0.0" 1206 | }, 1207 | "engines": { 1208 | "node": ">=10" 1209 | }, 1210 | "funding": { 1211 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1212 | } 1213 | }, 1214 | "node_modules/wrappy": { 1215 | "version": "1.0.2", 1216 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1217 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1218 | "dev": true 1219 | }, 1220 | "node_modules/xtend": { 1221 | "version": "4.0.2", 1222 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1223 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1224 | "dev": true, 1225 | "engines": { 1226 | "node": ">=0.4" 1227 | } 1228 | }, 1229 | "node_modules/y18n": { 1230 | "version": "5.0.8", 1231 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1232 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1233 | "dev": true, 1234 | "engines": { 1235 | "node": ">=10" 1236 | } 1237 | }, 1238 | "node_modules/yargs": { 1239 | "version": "16.2.0", 1240 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1241 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1242 | "dev": true, 1243 | "dependencies": { 1244 | "cliui": "^7.0.2", 1245 | "escalade": "^3.1.1", 1246 | "get-caller-file": "^2.0.5", 1247 | "require-directory": "^2.1.1", 1248 | "string-width": "^4.2.0", 1249 | "y18n": "^5.0.5", 1250 | "yargs-parser": "^20.2.2" 1251 | }, 1252 | "engines": { 1253 | "node": ">=10" 1254 | } 1255 | }, 1256 | "node_modules/yargs-parser": { 1257 | "version": "20.2.9", 1258 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 1259 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 1260 | "dev": true, 1261 | "engines": { 1262 | "node": ">=10" 1263 | } 1264 | } 1265 | }, 1266 | "dependencies": { 1267 | "@esbuild/linux-loong64": { 1268 | "version": "0.14.54", 1269 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", 1270 | "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", 1271 | "dev": true, 1272 | "optional": true 1273 | }, 1274 | "@simonwep/pickr": { 1275 | "version": "1.8.2", 1276 | "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz", 1277 | "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==", 1278 | "requires": { 1279 | "core-js": "^3.15.1", 1280 | "nanopop": "^2.1.0" 1281 | } 1282 | }, 1283 | "ansi-regex": { 1284 | "version": "5.0.1", 1285 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1286 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1287 | "dev": true 1288 | }, 1289 | "ansi-styles": { 1290 | "version": "4.3.0", 1291 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1292 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1293 | "dev": true, 1294 | "requires": { 1295 | "color-convert": "^2.0.1" 1296 | } 1297 | }, 1298 | "async": { 1299 | "version": "2.6.4", 1300 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", 1301 | "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", 1302 | "dev": true, 1303 | "requires": { 1304 | "lodash": "^4.17.14" 1305 | } 1306 | }, 1307 | "balanced-match": { 1308 | "version": "1.0.2", 1309 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1310 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1311 | "dev": true 1312 | }, 1313 | "basic-auth": { 1314 | "version": "2.0.1", 1315 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 1316 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 1317 | "dev": true, 1318 | "requires": { 1319 | "safe-buffer": "5.1.2" 1320 | } 1321 | }, 1322 | "brace-expansion": { 1323 | "version": "1.1.11", 1324 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1325 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1326 | "dev": true, 1327 | "requires": { 1328 | "balanced-match": "^1.0.0", 1329 | "concat-map": "0.0.1" 1330 | } 1331 | }, 1332 | "call-bind": { 1333 | "version": "1.0.2", 1334 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 1335 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 1336 | "dev": true, 1337 | "requires": { 1338 | "function-bind": "^1.1.1", 1339 | "get-intrinsic": "^1.0.2" 1340 | } 1341 | }, 1342 | "chalk": { 1343 | "version": "4.1.2", 1344 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1345 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1346 | "dev": true, 1347 | "requires": { 1348 | "ansi-styles": "^4.1.0", 1349 | "supports-color": "^7.1.0" 1350 | } 1351 | }, 1352 | "cliui": { 1353 | "version": "7.0.4", 1354 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1355 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1356 | "dev": true, 1357 | "requires": { 1358 | "string-width": "^4.2.0", 1359 | "strip-ansi": "^6.0.0", 1360 | "wrap-ansi": "^7.0.0" 1361 | } 1362 | }, 1363 | "color-convert": { 1364 | "version": "2.0.1", 1365 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1366 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1367 | "dev": true, 1368 | "requires": { 1369 | "color-name": "~1.1.4" 1370 | } 1371 | }, 1372 | "color-name": { 1373 | "version": "1.1.4", 1374 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1375 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1376 | "dev": true 1377 | }, 1378 | "concat-map": { 1379 | "version": "0.0.1", 1380 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1381 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1382 | "dev": true 1383 | }, 1384 | "copyfiles": { 1385 | "version": "2.4.1", 1386 | "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", 1387 | "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", 1388 | "dev": true, 1389 | "requires": { 1390 | "glob": "^7.0.5", 1391 | "minimatch": "^3.0.3", 1392 | "mkdirp": "^1.0.4", 1393 | "noms": "0.0.0", 1394 | "through2": "^2.0.1", 1395 | "untildify": "^4.0.0", 1396 | "yargs": "^16.1.0" 1397 | } 1398 | }, 1399 | "core-js": { 1400 | "version": "3.25.2", 1401 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.2.tgz", 1402 | "integrity": "sha512-YB4IAT1bjEfxTJ1XYy11hJAKskO+qmhuDBM8/guIfMz4JvdsAQAqvyb97zXX7JgSrfPLG5mRGFWJwJD39ruq2A==" 1403 | }, 1404 | "core-util-is": { 1405 | "version": "1.0.3", 1406 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 1407 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", 1408 | "dev": true 1409 | }, 1410 | "corser": { 1411 | "version": "2.0.1", 1412 | "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", 1413 | "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", 1414 | "dev": true 1415 | }, 1416 | "debug": { 1417 | "version": "3.2.7", 1418 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1419 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1420 | "dev": true, 1421 | "requires": { 1422 | "ms": "^2.1.1" 1423 | } 1424 | }, 1425 | "draco3d": { 1426 | "version": "1.5.3", 1427 | "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.3.tgz", 1428 | "integrity": "sha512-Ahum6SewAd1oVMm6Fk8T/zCE0qbzjohhO5pl1Xp5Outl4JKv7jYicfd5vNtkzImx94XE35fhNXVqHk9ajt+6Tg==" 1429 | }, 1430 | "emoji-regex": { 1431 | "version": "8.0.0", 1432 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1433 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1434 | "dev": true 1435 | }, 1436 | "esbuild": { 1437 | "version": "0.14.54", 1438 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", 1439 | "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", 1440 | "dev": true, 1441 | "requires": { 1442 | "@esbuild/linux-loong64": "0.14.54", 1443 | "esbuild-android-64": "0.14.54", 1444 | "esbuild-android-arm64": "0.14.54", 1445 | "esbuild-darwin-64": "0.14.54", 1446 | "esbuild-darwin-arm64": "0.14.54", 1447 | "esbuild-freebsd-64": "0.14.54", 1448 | "esbuild-freebsd-arm64": "0.14.54", 1449 | "esbuild-linux-32": "0.14.54", 1450 | "esbuild-linux-64": "0.14.54", 1451 | "esbuild-linux-arm": "0.14.54", 1452 | "esbuild-linux-arm64": "0.14.54", 1453 | "esbuild-linux-mips64le": "0.14.54", 1454 | "esbuild-linux-ppc64le": "0.14.54", 1455 | "esbuild-linux-riscv64": "0.14.54", 1456 | "esbuild-linux-s390x": "0.14.54", 1457 | "esbuild-netbsd-64": "0.14.54", 1458 | "esbuild-openbsd-64": "0.14.54", 1459 | "esbuild-sunos-64": "0.14.54", 1460 | "esbuild-windows-32": "0.14.54", 1461 | "esbuild-windows-64": "0.14.54", 1462 | "esbuild-windows-arm64": "0.14.54" 1463 | } 1464 | }, 1465 | "esbuild-android-64": { 1466 | "version": "0.14.54", 1467 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", 1468 | "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", 1469 | "dev": true, 1470 | "optional": true 1471 | }, 1472 | "esbuild-android-arm64": { 1473 | "version": "0.14.54", 1474 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", 1475 | "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", 1476 | "dev": true, 1477 | "optional": true 1478 | }, 1479 | "esbuild-darwin-64": { 1480 | "version": "0.14.54", 1481 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", 1482 | "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", 1483 | "dev": true, 1484 | "optional": true 1485 | }, 1486 | "esbuild-darwin-arm64": { 1487 | "version": "0.14.54", 1488 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", 1489 | "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", 1490 | "dev": true, 1491 | "optional": true 1492 | }, 1493 | "esbuild-freebsd-64": { 1494 | "version": "0.14.54", 1495 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", 1496 | "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", 1497 | "dev": true, 1498 | "optional": true 1499 | }, 1500 | "esbuild-freebsd-arm64": { 1501 | "version": "0.14.54", 1502 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", 1503 | "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", 1504 | "dev": true, 1505 | "optional": true 1506 | }, 1507 | "esbuild-linux-32": { 1508 | "version": "0.14.54", 1509 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", 1510 | "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", 1511 | "dev": true, 1512 | "optional": true 1513 | }, 1514 | "esbuild-linux-64": { 1515 | "version": "0.14.54", 1516 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", 1517 | "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", 1518 | "dev": true, 1519 | "optional": true 1520 | }, 1521 | "esbuild-linux-arm": { 1522 | "version": "0.14.54", 1523 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", 1524 | "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", 1525 | "dev": true, 1526 | "optional": true 1527 | }, 1528 | "esbuild-linux-arm64": { 1529 | "version": "0.14.54", 1530 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", 1531 | "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", 1532 | "dev": true, 1533 | "optional": true 1534 | }, 1535 | "esbuild-linux-mips64le": { 1536 | "version": "0.14.54", 1537 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", 1538 | "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", 1539 | "dev": true, 1540 | "optional": true 1541 | }, 1542 | "esbuild-linux-ppc64le": { 1543 | "version": "0.14.54", 1544 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", 1545 | "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", 1546 | "dev": true, 1547 | "optional": true 1548 | }, 1549 | "esbuild-linux-riscv64": { 1550 | "version": "0.14.54", 1551 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", 1552 | "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", 1553 | "dev": true, 1554 | "optional": true 1555 | }, 1556 | "esbuild-linux-s390x": { 1557 | "version": "0.14.54", 1558 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", 1559 | "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", 1560 | "dev": true, 1561 | "optional": true 1562 | }, 1563 | "esbuild-netbsd-64": { 1564 | "version": "0.14.54", 1565 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", 1566 | "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", 1567 | "dev": true, 1568 | "optional": true 1569 | }, 1570 | "esbuild-openbsd-64": { 1571 | "version": "0.14.54", 1572 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", 1573 | "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", 1574 | "dev": true, 1575 | "optional": true 1576 | }, 1577 | "esbuild-sunos-64": { 1578 | "version": "0.14.54", 1579 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", 1580 | "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", 1581 | "dev": true, 1582 | "optional": true 1583 | }, 1584 | "esbuild-windows-32": { 1585 | "version": "0.14.54", 1586 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", 1587 | "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", 1588 | "dev": true, 1589 | "optional": true 1590 | }, 1591 | "esbuild-windows-64": { 1592 | "version": "0.14.54", 1593 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", 1594 | "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", 1595 | "dev": true, 1596 | "optional": true 1597 | }, 1598 | "esbuild-windows-arm64": { 1599 | "version": "0.14.54", 1600 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", 1601 | "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", 1602 | "dev": true, 1603 | "optional": true 1604 | }, 1605 | "escalade": { 1606 | "version": "3.1.1", 1607 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1608 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1609 | "dev": true 1610 | }, 1611 | "eventemitter3": { 1612 | "version": "4.0.7", 1613 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", 1614 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", 1615 | "dev": true 1616 | }, 1617 | "fflate": { 1618 | "version": "0.7.3", 1619 | "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.3.tgz", 1620 | "integrity": "sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==" 1621 | }, 1622 | "follow-redirects": { 1623 | "version": "1.15.2", 1624 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 1625 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 1626 | "dev": true 1627 | }, 1628 | "fs.realpath": { 1629 | "version": "1.0.0", 1630 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1631 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1632 | "dev": true 1633 | }, 1634 | "function-bind": { 1635 | "version": "1.1.1", 1636 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1637 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1638 | "dev": true 1639 | }, 1640 | "get-caller-file": { 1641 | "version": "2.0.5", 1642 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1643 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1644 | "dev": true 1645 | }, 1646 | "get-intrinsic": { 1647 | "version": "1.1.3", 1648 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 1649 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 1650 | "dev": true, 1651 | "requires": { 1652 | "function-bind": "^1.1.1", 1653 | "has": "^1.0.3", 1654 | "has-symbols": "^1.0.3" 1655 | } 1656 | }, 1657 | "glob": { 1658 | "version": "7.2.3", 1659 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1660 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1661 | "dev": true, 1662 | "requires": { 1663 | "fs.realpath": "^1.0.0", 1664 | "inflight": "^1.0.4", 1665 | "inherits": "2", 1666 | "minimatch": "^3.1.1", 1667 | "once": "^1.3.0", 1668 | "path-is-absolute": "^1.0.0" 1669 | } 1670 | }, 1671 | "has": { 1672 | "version": "1.0.3", 1673 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1674 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1675 | "dev": true, 1676 | "requires": { 1677 | "function-bind": "^1.1.1" 1678 | } 1679 | }, 1680 | "has-flag": { 1681 | "version": "4.0.0", 1682 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1683 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1684 | "dev": true 1685 | }, 1686 | "has-symbols": { 1687 | "version": "1.0.3", 1688 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 1689 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 1690 | "dev": true 1691 | }, 1692 | "he": { 1693 | "version": "1.2.0", 1694 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1695 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1696 | "dev": true 1697 | }, 1698 | "html-encoding-sniffer": { 1699 | "version": "3.0.0", 1700 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", 1701 | "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", 1702 | "dev": true, 1703 | "requires": { 1704 | "whatwg-encoding": "^2.0.0" 1705 | } 1706 | }, 1707 | "http-proxy": { 1708 | "version": "1.18.1", 1709 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", 1710 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", 1711 | "dev": true, 1712 | "requires": { 1713 | "eventemitter3": "^4.0.0", 1714 | "follow-redirects": "^1.0.0", 1715 | "requires-port": "^1.0.0" 1716 | } 1717 | }, 1718 | "http-server": { 1719 | "version": "14.1.1", 1720 | "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", 1721 | "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", 1722 | "dev": true, 1723 | "requires": { 1724 | "basic-auth": "^2.0.1", 1725 | "chalk": "^4.1.2", 1726 | "corser": "^2.0.1", 1727 | "he": "^1.2.0", 1728 | "html-encoding-sniffer": "^3.0.0", 1729 | "http-proxy": "^1.18.1", 1730 | "mime": "^1.6.0", 1731 | "minimist": "^1.2.6", 1732 | "opener": "^1.5.1", 1733 | "portfinder": "^1.0.28", 1734 | "secure-compare": "3.0.1", 1735 | "union": "~0.5.0", 1736 | "url-join": "^4.0.1" 1737 | } 1738 | }, 1739 | "iconv-lite": { 1740 | "version": "0.6.3", 1741 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 1742 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 1743 | "dev": true, 1744 | "requires": { 1745 | "safer-buffer": ">= 2.1.2 < 3.0.0" 1746 | } 1747 | }, 1748 | "inflight": { 1749 | "version": "1.0.6", 1750 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1751 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1752 | "dev": true, 1753 | "requires": { 1754 | "once": "^1.3.0", 1755 | "wrappy": "1" 1756 | } 1757 | }, 1758 | "inherits": { 1759 | "version": "2.0.4", 1760 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1761 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1762 | "dev": true 1763 | }, 1764 | "is-fullwidth-code-point": { 1765 | "version": "3.0.0", 1766 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1767 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1768 | "dev": true 1769 | }, 1770 | "isarray": { 1771 | "version": "0.0.1", 1772 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1773 | "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", 1774 | "dev": true 1775 | }, 1776 | "lodash": { 1777 | "version": "4.17.21", 1778 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1779 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 1780 | "dev": true 1781 | }, 1782 | "mime": { 1783 | "version": "1.6.0", 1784 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1785 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1786 | "dev": true 1787 | }, 1788 | "minimatch": { 1789 | "version": "3.1.2", 1790 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1791 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1792 | "dev": true, 1793 | "requires": { 1794 | "brace-expansion": "^1.1.7" 1795 | } 1796 | }, 1797 | "minimist": { 1798 | "version": "1.2.6", 1799 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 1800 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", 1801 | "dev": true 1802 | }, 1803 | "mkdirp": { 1804 | "version": "1.0.4", 1805 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 1806 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 1807 | "dev": true 1808 | }, 1809 | "ms": { 1810 | "version": "2.1.3", 1811 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1812 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1813 | "dev": true 1814 | }, 1815 | "nanopop": { 1816 | "version": "2.2.0", 1817 | "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.2.0.tgz", 1818 | "integrity": "sha512-E9JaHcxh3ere8/BEZHAcnuD10RluTSPyTToBvoFWS9/7DcCx6gyKjbn7M7Bx7E1veCxCuY1iO6h4+gdAf1j73Q==" 1819 | }, 1820 | "noms": { 1821 | "version": "0.0.0", 1822 | "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", 1823 | "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", 1824 | "dev": true, 1825 | "requires": { 1826 | "inherits": "^2.0.1", 1827 | "readable-stream": "~1.0.31" 1828 | } 1829 | }, 1830 | "object-inspect": { 1831 | "version": "1.12.2", 1832 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 1833 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", 1834 | "dev": true 1835 | }, 1836 | "occt-import-js": { 1837 | "version": "0.0.10", 1838 | "resolved": "https://registry.npmjs.org/occt-import-js/-/occt-import-js-0.0.10.tgz", 1839 | "integrity": "sha512-DvFitPZswkOqkkM7YvuIlykHp30e3Da1f83hVdVQ0WegcwNe933RF2/iQ0AnpO/T0SNnfjXSA0d68My8XWIr+Q==" 1840 | }, 1841 | "once": { 1842 | "version": "1.4.0", 1843 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1844 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1845 | "dev": true, 1846 | "requires": { 1847 | "wrappy": "1" 1848 | } 1849 | }, 1850 | "online-3d-viewer": { 1851 | "version": "0.8.16", 1852 | "resolved": "https://registry.npmjs.org/online-3d-viewer/-/online-3d-viewer-0.8.16.tgz", 1853 | "integrity": "sha512-CKSMNzRuJytz818ZpOg0s8eolhS+6z8ams2jFBJ8uWzky+uglZGoBj0r97wTIz26jijb5nSY+hnV9o+KZsnV8w==", 1854 | "requires": { 1855 | "@simonwep/pickr": "1.8.2", 1856 | "draco3d": "1.5.3", 1857 | "fflate": "0.7.3", 1858 | "occt-import-js": "0.0.10", 1859 | "rhino3dm": "7.15.0", 1860 | "three": "0.144.0", 1861 | "web-ifc": "0.0.35" 1862 | } 1863 | }, 1864 | "opener": { 1865 | "version": "1.5.2", 1866 | "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", 1867 | "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", 1868 | "dev": true 1869 | }, 1870 | "path-is-absolute": { 1871 | "version": "1.0.1", 1872 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1873 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1874 | "dev": true 1875 | }, 1876 | "portfinder": { 1877 | "version": "1.0.32", 1878 | "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", 1879 | "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", 1880 | "dev": true, 1881 | "requires": { 1882 | "async": "^2.6.4", 1883 | "debug": "^3.2.7", 1884 | "mkdirp": "^0.5.6" 1885 | }, 1886 | "dependencies": { 1887 | "mkdirp": { 1888 | "version": "0.5.6", 1889 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 1890 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 1891 | "dev": true, 1892 | "requires": { 1893 | "minimist": "^1.2.6" 1894 | } 1895 | } 1896 | } 1897 | }, 1898 | "process-nextick-args": { 1899 | "version": "2.0.1", 1900 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1901 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 1902 | "dev": true 1903 | }, 1904 | "qs": { 1905 | "version": "6.11.0", 1906 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 1907 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1908 | "dev": true, 1909 | "requires": { 1910 | "side-channel": "^1.0.4" 1911 | } 1912 | }, 1913 | "readable-stream": { 1914 | "version": "1.0.34", 1915 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1916 | "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", 1917 | "dev": true, 1918 | "requires": { 1919 | "core-util-is": "~1.0.0", 1920 | "inherits": "~2.0.1", 1921 | "isarray": "0.0.1", 1922 | "string_decoder": "~0.10.x" 1923 | } 1924 | }, 1925 | "require-directory": { 1926 | "version": "2.1.1", 1927 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1928 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1929 | "dev": true 1930 | }, 1931 | "requires-port": { 1932 | "version": "1.0.0", 1933 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 1934 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", 1935 | "dev": true 1936 | }, 1937 | "rhino3dm": { 1938 | "version": "7.15.0", 1939 | "resolved": "https://registry.npmjs.org/rhino3dm/-/rhino3dm-7.15.0.tgz", 1940 | "integrity": "sha512-EOYK79KqgWNvonzXRH3eSO5Qmz1aBUzFvEZad2JUMTGq65lNOOU/LoUgFCJ0P/EZNdMJD+pNTdn9zgANOautgw==" 1941 | }, 1942 | "safe-buffer": { 1943 | "version": "5.1.2", 1944 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1945 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1946 | "dev": true 1947 | }, 1948 | "safer-buffer": { 1949 | "version": "2.1.2", 1950 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1951 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1952 | "dev": true 1953 | }, 1954 | "secure-compare": { 1955 | "version": "3.0.1", 1956 | "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", 1957 | "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", 1958 | "dev": true 1959 | }, 1960 | "side-channel": { 1961 | "version": "1.0.4", 1962 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1963 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1964 | "dev": true, 1965 | "requires": { 1966 | "call-bind": "^1.0.0", 1967 | "get-intrinsic": "^1.0.2", 1968 | "object-inspect": "^1.9.0" 1969 | } 1970 | }, 1971 | "string_decoder": { 1972 | "version": "0.10.31", 1973 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1974 | "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", 1975 | "dev": true 1976 | }, 1977 | "string-width": { 1978 | "version": "4.2.3", 1979 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1980 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1981 | "dev": true, 1982 | "requires": { 1983 | "emoji-regex": "^8.0.0", 1984 | "is-fullwidth-code-point": "^3.0.0", 1985 | "strip-ansi": "^6.0.1" 1986 | } 1987 | }, 1988 | "strip-ansi": { 1989 | "version": "6.0.1", 1990 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1991 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1992 | "dev": true, 1993 | "requires": { 1994 | "ansi-regex": "^5.0.1" 1995 | } 1996 | }, 1997 | "supports-color": { 1998 | "version": "7.2.0", 1999 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2000 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2001 | "dev": true, 2002 | "requires": { 2003 | "has-flag": "^4.0.0" 2004 | } 2005 | }, 2006 | "three": { 2007 | "version": "0.144.0", 2008 | "resolved": "https://registry.npmjs.org/three/-/three-0.144.0.tgz", 2009 | "integrity": "sha512-R8AXPuqfjfRJKkYoTQcTK7A6i3AdO9++2n8ubya/GTU+fEHhYKu1ZooRSCPkx69jbnzT7dD/xEo6eROQTt2lJw==" 2010 | }, 2011 | "through2": { 2012 | "version": "2.0.5", 2013 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", 2014 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", 2015 | "dev": true, 2016 | "requires": { 2017 | "readable-stream": "~2.3.6", 2018 | "xtend": "~4.0.1" 2019 | }, 2020 | "dependencies": { 2021 | "isarray": { 2022 | "version": "1.0.0", 2023 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 2024 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", 2025 | "dev": true 2026 | }, 2027 | "readable-stream": { 2028 | "version": "2.3.7", 2029 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 2030 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 2031 | "dev": true, 2032 | "requires": { 2033 | "core-util-is": "~1.0.0", 2034 | "inherits": "~2.0.3", 2035 | "isarray": "~1.0.0", 2036 | "process-nextick-args": "~2.0.0", 2037 | "safe-buffer": "~5.1.1", 2038 | "string_decoder": "~1.1.1", 2039 | "util-deprecate": "~1.0.1" 2040 | } 2041 | }, 2042 | "string_decoder": { 2043 | "version": "1.1.1", 2044 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 2045 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 2046 | "dev": true, 2047 | "requires": { 2048 | "safe-buffer": "~5.1.0" 2049 | } 2050 | } 2051 | } 2052 | }, 2053 | "union": { 2054 | "version": "0.5.0", 2055 | "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", 2056 | "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", 2057 | "dev": true, 2058 | "requires": { 2059 | "qs": "^6.4.0" 2060 | } 2061 | }, 2062 | "untildify": { 2063 | "version": "4.0.0", 2064 | "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", 2065 | "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", 2066 | "dev": true 2067 | }, 2068 | "url-join": { 2069 | "version": "4.0.1", 2070 | "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", 2071 | "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", 2072 | "dev": true 2073 | }, 2074 | "util-deprecate": { 2075 | "version": "1.0.2", 2076 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2077 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 2078 | "dev": true 2079 | }, 2080 | "web-ifc": { 2081 | "version": "0.0.35", 2082 | "resolved": "https://registry.npmjs.org/web-ifc/-/web-ifc-0.0.35.tgz", 2083 | "integrity": "sha512-if/L9RiJAD+jJ1oWtHj44+o2NG6K6azK0otE9Pbppr5hNBnouYfJQNmnHZ+Ya+wZeaByo9KeU64D60Y6lFnT4w==" 2084 | }, 2085 | "whatwg-encoding": { 2086 | "version": "2.0.0", 2087 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", 2088 | "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", 2089 | "dev": true, 2090 | "requires": { 2091 | "iconv-lite": "0.6.3" 2092 | } 2093 | }, 2094 | "wrap-ansi": { 2095 | "version": "7.0.0", 2096 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 2097 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 2098 | "dev": true, 2099 | "requires": { 2100 | "ansi-styles": "^4.0.0", 2101 | "string-width": "^4.1.0", 2102 | "strip-ansi": "^6.0.0" 2103 | } 2104 | }, 2105 | "wrappy": { 2106 | "version": "1.0.2", 2107 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2108 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 2109 | "dev": true 2110 | }, 2111 | "xtend": { 2112 | "version": "4.0.2", 2113 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 2114 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 2115 | "dev": true 2116 | }, 2117 | "y18n": { 2118 | "version": "5.0.8", 2119 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 2120 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 2121 | "dev": true 2122 | }, 2123 | "yargs": { 2124 | "version": "16.2.0", 2125 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 2126 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 2127 | "dev": true, 2128 | "requires": { 2129 | "cliui": "^7.0.2", 2130 | "escalade": "^3.1.1", 2131 | "get-caller-file": "^2.0.5", 2132 | "require-directory": "^2.1.1", 2133 | "string-width": "^4.2.0", 2134 | "y18n": "^5.0.5", 2135 | "yargs-parser": "^20.2.2" 2136 | } 2137 | }, 2138 | "yargs-parser": { 2139 | "version": "20.2.9", 2140 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 2141 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 2142 | "dev": true 2143 | } 2144 | } 2145 | } 2146 | -------------------------------------------------------------------------------- /use_as_es6_module/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "online-3d-viewer-example-esm", 3 | "description": "Online 3D Viewer Example", 4 | "version": "0.0.1", 5 | "repository": "github:kovacsv/Online3DViewerExample", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "npm run build && http-server", 9 | "copylibs": "copyfiles --up 3 node_modules/online-3d-viewer/libs/loaders/* build/libs", 10 | "copyenvmaps": "copyfiles --up 5 node_modules/online-3d-viewer/website/assets/envmaps/**/* build/envmaps", 11 | "build": "esbuild source/main.js --bundle --minify --sourcemap --outfile=build/bundle.js && npm run copylibs && npm run copyenvmaps" 12 | }, 13 | "devDependencies": { 14 | "copyfiles": "^2.4.1", 15 | "esbuild": "^0.14.23", 16 | "http-server": "^14.0.0" 17 | }, 18 | "dependencies": { 19 | "online-3d-viewer": "*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /use_as_es6_module/source/main.js: -------------------------------------------------------------------------------- 1 | import * as OV from 'online-3d-viewer'; 2 | 3 | // Set the external libraries location to the 4 | // folder it copied during build. 5 | OV.SetExternalLibLocation ('build/libs'); 6 | 7 | // Init all the 3d viewer elements. 8 | OV.Init3DViewerElements (); 9 | -------------------------------------------------------------------------------- /use_as_es6_module/start_server.bat: -------------------------------------------------------------------------------- 1 | npm start 2 | -------------------------------------------------------------------------------- /use_as_react_component/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /use_as_react_component/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /use_as_react_component/.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | node_modules 3 | public/libs -------------------------------------------------------------------------------- /use_as_react_component/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { "trailingComma": "es5", "tabWidth": 4, "semi": true, "singleQuote": false } 2 | -------------------------------------------------------------------------------- /use_as_react_component/README.md: -------------------------------------------------------------------------------- 1 | # Online 3D Viewer with Next.js 2 | 3 | This example and documentation was authored by [callum-gander](https://github.com/callum-gander), big thanks for the participation. 4 | 5 | ## This is a work in progress! If you want to add or correct these examples, please submit a pull request! 6 | 7 | There was a lack of documentation for Online 3D Viewer 8 | and how to use it with React and Next.js, so I've made 9 | this guide to help others encountering the same issue. 10 | I'll show some examples of how you can use the Online 3D 11 | Viewer and while also explaining some errors and issues 12 | I encountered and how to resolve them. This is very much 13 | a work in progress and isn't perfect, if you have any 14 | feedback or additional advice, please submit a pull 15 | request 16 | 17 | To setup this project, clone the repo, npm install the 18 | packages and run the server with npm run dev. I've only 19 | tested this with stl and 3dm files, other files may 20 | produce unique issues, if you encounter them, please let 21 | me know 22 | 23 | **Important note for Windows users:** If you get errors running 24 | the above commands, try to run them from a powershell console. 25 | 26 | ## Setting up Online 3D Viewer with your Next app: 27 | 28 | First, install the npm package from 29 | https://www.npmjs.com/package/online-3d-viewer. Import 30 | the package into your project with `import * as OV from "online-3d-viewer"` 31 | 32 | Secondly, on order for this package to work in your 33 | project, make sure you have the libs folder in your 34 | public folder, as shown in this repo. These are the 35 | external packages used by Online-3D-Viewer to load the 36 | different types of 3D file client side. 37 | **THE PACKAGE WILL NOT WORK WITHOUT THESE PRESENT.** 38 | 39 | This is all the setup you'll need and we'll now look at 40 | the different ways you can use the viewer and load in 41 | files 42 | 43 | ## Example 1: A basic 3D viewer 44 | 45 | This is a very basic example of how you can have a file 46 | input, upload a file and have it display in the 3D 47 | viewer. This example will also clean up correctly, 48 | avoiding memory leaks that can happen while using this 49 | package with React . Using this example's component, you 50 | can easily spin up multiple different viewers at the 51 | same time and passing different files into each one, 52 | though I would be careful doing so for reasons mentioned 53 | at the bottom of this page. 54 | 55 | See the "Basic3DViewer" component in the components 56 | folder to see how this was constructed. I've left 57 | annotated notes explaining how everything works but will 58 | add some additional comments here 59 | 60 | ``` 61 | import { useEffect, useRef, useState } from "react"; 62 | import * as OV from "online-3d-viewer"; 63 | 64 | const Basic3DViewer = ({ file }) => { 65 | // The viewer is attached to parentDiv.current 66 | const parentDiv = useRef(null); 67 | // However, this really attaches the canvas element to the parentDiv, we need to also keep a reference for the viewer object itself which we'll do with viewerRef 68 | const viewerRef = useRef(null); 69 | 70 | useEffect(() => { 71 | // If there is a file passed in that isn't null, instantiate the viewer object 72 | if (file) { 73 | // Set the location of the libraries needed to load different models to lib, which in nextjs will point to "/public/libs" 74 | OV.SetExternalLibLocation("libs"); 75 | // Initialise all the 3D viewer elements 76 | OV.Init3DViewerElements(); 77 | // Before initialising the viewer, check that there isn't already a viewer object attached to viewerRef 78 | if (viewerRef.current === null) { 79 | // This is fairly self explanatory, initialise the viewer object with reasonable defaults. See the documentation for this component to see what other options you have 80 | let viewer = new OV.EmbeddedViewer(parentDiv.current, { 81 | camera: new OV.Camera( 82 | new OV.Coord3D(-150.0, 200.0, 300.0), 83 | new OV.Coord3D(0.0, 0.0, 0.0), 84 | new OV.Coord3D(0.0, 1.0, 0.0), 85 | 45.0 86 | ), 87 | backgroundColor: new OV.RGBAColor(255, 255, 255, 255), 88 | defaultColor: new OV.RGBColor(0, 100, 100), 89 | edgeSettings: { 90 | showEdges: false, 91 | edgeThreshold: 1, 92 | }, 93 | environmentMap: [ 94 | "../website/assets/envmaps/grayclouds/posx.jpg", 95 | "../website/assets/envmaps/grayclouds/negx.jpg", 96 | "../website/assets/envmaps/grayclouds/posy.jpg", 97 | "../website/assets/envmaps/grayclouds/negy.jpg", 98 | "../website/assets/envmaps/grayclouds/posz.jpg", 99 | "../website/assets/envmaps/grayclouds/negz.jpg", 100 | ], 101 | onModelLoaded: () => { 102 | console.log(viewerRef.current.GetViewer()); 103 | }, 104 | }); 105 | // ! This feels stupid but unfortunately, this resizing event can persist after clean up and lead to an error, one way to avoid this happening is to just overwrite the method so that it doesn't call this.viewer 106 | // ! Ideally, you'd clean it up during cleanup but I just found it easier to replace and ignore this event instead 107 | viewer.Resize = () => { 108 | console.log("I'm not resizing"); 109 | }; 110 | 111 | // Here, we assign the viewer object viewerRef.current to keep hold of it for later use 112 | viewerRef.current = viewer; 113 | 114 | // I've found This method of loading the file into the viewer works most reliably 115 | // Create a new data transfer object, add a File interface to it's items, grab the files and assign them to file_list and then load the model into the viewer with them 116 | var dt = new DataTransfer(); 117 | dt.items.add(file); 118 | var file_list = dt.files; 119 | viewer.LoadModelFromFileList(file_list); 120 | } 121 | } 122 | 123 | return () => { 124 | // ! We need to correctly clean up our viewer, it's listeners and related model data to ensure memory leaks don't occur 125 | // ! If you want to see what can happen if this isn't here, comment out this code and repeatedly spin up multiple viewers and then do a heap snapshot with chrome and you will see a massive amount of data that isn't being cleaned up by the garbage collector 126 | // We first check that both the viewerRef and parentDiv aren't null so we don't call a method on an object that doesn't exist 127 | if (viewerRef.current !== null && parentDiv.current !== null) { 128 | // ! I threw the kitchen sink at this to get rid of the memory leaks so some of this code is definitely redundant and there is likely a cleaner way of doing this 129 | // We delete the model, reset the state of the renderer and clear the viewer 130 | delete viewerRef.current.model; 131 | viewerRef.current.viewer.renderer.resetState(); 132 | viewerRef.current.viewer.Clear(); 133 | // Then we delete the whole viewer object 134 | delete viewerRef.current.viewer; 135 | // We grab canvas element before we delete it to ensure we lose the webgl context and it doesn't persist 136 | const gl = viewerRef.current.canvas.getContext("webgl2"); 137 | gl.getExtension("WEBGL_lose_context").loseContext(); 138 | // We replace the canvas element which will also replace all the event listeners which can cause the element and things associated with it to not be correctly cleaned up by the garbage collector 139 | const tempClone = viewerRef.current.canvas.cloneNode(true); 140 | viewerRef.current.canvas.parentNode.replaceChild( 141 | tempClone, 142 | viewerRef.current.canvas 143 | ); 144 | // Finally, we delete the canvas element and set the viewerRef.current to null, meaning everything should be properly cleaned up 145 | parentDiv.current.removeChild(parentDiv.current.children[0]); 146 | viewerRef.current = null; 147 | } 148 | }; 149 | }, [file]); 150 | 151 | return ( 152 | <> 153 |
159 | 160 | ); 161 | }; 162 | 163 | export default Basic3DViewer; 164 | 165 | ``` 166 | 167 | You may notice that the method of loading files seems 168 | over complicated. As the viewer loads files in using a 169 | FileList, you don't need to extract the file, put it in 170 | state, then use a DataTransfer object to recreate a 171 | FileList. However, this method works better if you're 172 | holding File Interfaces in state, e.g. with Zustand, and 173 | passing them between components, as this way the 174 | original file input DOM element doesn't need to be on 175 | the page. This is useful using UI patterns like Wizards, 176 | where a user may upload a file on one step and then the 177 | app may need the file again on another step despite the 178 | fact that DOM input is gone 179 | 180 | ## Example 2: Different methods to load files 181 | 182 | There are two methods that I've used to load files into 183 | the viewer: LoadModelFromFileList and 184 | LoadModelFromInputFiles. If you need to load a file 185 | uploaded through a DOM input by the user, use 186 | LoadModelFromFileList and use the prior example. If you 187 | want to load the file from a URL, use the example below. 188 | 189 | As with the prior example, please see the 190 | "ViewerWithUrls" component and see the annotated code. 191 | 192 | ``` 193 | import { useEffect, useRef, useState } from "react"; 194 | import * as OV from "online-3d-viewer"; 195 | 196 | const ViewerWithUrls = ({ url, loadModel }) => { 197 | const parentDiv = useRef(null); 198 | const viewerRef = useRef(null); 199 | 200 | useEffect(() => { 201 | if (url && loadModel) { 202 | OV.SetExternalLibLocation("libs"); 203 | OV.Init3DViewerElements(); 204 | if (viewerRef.current === null) { 205 | let viewer = new OV.EmbeddedViewer(parentDiv.current, { 206 | camera: new OV.Camera( 207 | new OV.Coord3D(-150.0, 200.0, 300.0), 208 | new OV.Coord3D(0.0, 0.0, 0.0), 209 | new OV.Coord3D(0.0, 1.0, 0.0), 210 | 45.0 211 | ), 212 | backgroundColor: new OV.RGBAColor(255, 255, 255, 255), 213 | defaultColor: new OV.RGBColor(0, 100, 100), 214 | edgeSettings: { 215 | showEdges: false, 216 | edgeThreshold: 1, 217 | }, 218 | environmentMap: [ 219 | "../website/assets/envmaps/grayclouds/posx.jpg", 220 | "../website/assets/envmaps/grayclouds/negx.jpg", 221 | "../website/assets/envmaps/grayclouds/posy.jpg", 222 | "../website/assets/envmaps/grayclouds/negy.jpg", 223 | "../website/assets/envmaps/grayclouds/posz.jpg", 224 | "../website/assets/envmaps/grayclouds/negz.jpg", 225 | ], 226 | onModelLoaded: () => { 227 | console.log(viewerRef.current.GetViewer()); 228 | }, 229 | }); 230 | viewer.Resize = () => { 231 | console.log("I'm not resizing"); 232 | }; 233 | 234 | // To load a file into the viewer using the url, we first pass a file name, OV.FileSource.Url and then the url of the model to the OV.InputFile constructor, put the newly created object in an array and save it as inputFiles 235 | let inputFiles = [ 236 | new OV.InputFile("test.stl", OV.FileSource.Url, url), 237 | ]; 238 | 239 | viewerRef.current = viewer; 240 | 241 | // Then we just pass inputFiles into the below method and viola 242 | viewer.LoadModelFromInputFiles(inputFiles); 243 | } 244 | } 245 | 246 | return () => { 247 | if (viewerRef.current !== null && parentDiv.current !== null) { 248 | delete viewerRef.current.model; 249 | viewerRef.current.viewer.renderer.resetState(); 250 | viewerRef.current.viewer.Clear(); 251 | delete viewerRef.current.viewer; 252 | const gl = viewerRef.current.canvas.getContext("webgl2"); 253 | gl.getExtension("WEBGL_lose_context").loseContext(); 254 | const tempClone = viewerRef.current.canvas.cloneNode(true); 255 | viewerRef.current.canvas.parentNode.replaceChild( 256 | tempClone, 257 | viewerRef.current.canvas 258 | ); 259 | parentDiv.current.removeChild(parentDiv.current.children[0]); 260 | // viewerRef.current.canvas.parentNode.removeChild(viewerRef.current.canvas); 261 | // viewerRef.current.canvas.remove() 262 | viewerRef.current = null; 263 | } 264 | }; 265 | }, [url, loadModel]); 266 | 267 | return ( 268 | <> 269 |
275 | 276 | ); 277 | }; 278 | 279 | export default ViewerWithUrls; 280 | 281 | ``` 282 | 283 | ### Example 3: Using viewer methods with your own UI 284 | 285 | Obviously, most users will likely want to use this 286 | package for more than just viewing 3D files. 287 | Unfortunately, there isn't any real documentation on how 288 | to do so and what can be done. I'll show some examples 289 | here but until there is more documentation, I'd advise 290 | you to read the code, particularly 291 | https://github.com/kovacsv/Online3DViewer/blob/master/source/engine/viewer/viewer.js 292 | and 293 | https://github.com/kovacsv/Online3DViewer/blob/master/source/engine/viewer/embeddedviewer.js 294 | to see what's possible 295 | 296 | I haven't commented the code for this component, it's 297 | fairly self explanatory. To better understand why 298 | certain parameters are being passed into each function, 299 | see the source code linked above 300 | 301 | ``` 302 | import { useEffect, useRef, useState } from "react"; 303 | import * as OV from "online-3d-viewer"; 304 | 305 | const ViewerWithUI = ({ file }) => { 306 | const parentDiv = useRef(null); 307 | const viewerRef = useRef(null); 308 | const [volume, setVolume] = useState(null); 309 | const [surfaceArea, setSurfaceArea] = useState(null); 310 | 311 | useEffect(() => { 312 | console.log(file); 313 | console.log(parentDiv); 314 | if (file) { 315 | OV.SetExternalLibLocation("libs"); 316 | OV.Init3DViewerElements(); 317 | // initialize the viewer with the parent element and some parameters 318 | if (viewerRef.current === null) { 319 | let viewer = new OV.EmbeddedViewer(parentDiv.current, { 320 | camera: new OV.Camera( 321 | new OV.Coord3D(-150.0, 200.0, 300.0), 322 | new OV.Coord3D(0.0, 0.0, 0.0), 323 | new OV.Coord3D(0.0, 1.0, 0.0), 324 | 45.0 325 | ), 326 | backgroundColor: new OV.RGBAColor(255, 255, 255, 255), 327 | defaultColor: new OV.RGBColor(0, 100, 100), 328 | edgeSettings: { 329 | showEdges: false, 330 | edgeThreshold: 1, 331 | }, 332 | environmentMap: [ 333 | "../website/assets/envmaps/grayclouds/posx.jpg", 334 | "../website/assets/envmaps/grayclouds/negx.jpg", 335 | "../website/assets/envmaps/grayclouds/posy.jpg", 336 | "../website/assets/envmaps/grayclouds/negy.jpg", 337 | "../website/assets/envmaps/grayclouds/posz.jpg", 338 | "../website/assets/envmaps/grayclouds/negz.jpg", 339 | ], 340 | onModelLoaded: () => { 341 | console.log(viewerRef.current.GetViewer()); 342 | }, 343 | }); 344 | // ! This feels stupid but unfortunately, this resizing event can persist after clean up and lead to an error, one way to avoid this happening is to just overwrite the method so that it doesn't call this.viewer 345 | viewer.Resize = () => { 346 | console.log("I'm not resizing"); 347 | }; 348 | viewerRef.current = viewer; 349 | var dt = new DataTransfer(); 350 | dt.items.add(file); 351 | var file_list = dt.files; 352 | viewer.LoadModelFromFileList(file_list); 353 | } 354 | } 355 | 356 | return () => { 357 | if (viewerRef.current !== null && parentDiv.current !== null) { 358 | delete viewerRef.current.model; 359 | viewerRef.current.viewer.renderer.resetState(); 360 | viewerRef.current.viewer.Clear(); 361 | delete viewerRef.current.viewer; 362 | const gl = viewerRef.current.canvas.getContext("webgl2"); 363 | gl.getExtension("WEBGL_lose_context").loseContext(); 364 | const tempClone = viewerRef.current.canvas.cloneNode(true); 365 | viewerRef.current.canvas.parentNode.replaceChild( 366 | tempClone, 367 | viewerRef.current.canvas 368 | ); 369 | parentDiv.current.removeChild(parentDiv.current.children[0]); 370 | // viewerRef.current.canvas.parentNode.removeChild(viewerRef.current.canvas); 371 | // viewerRef.current.canvas.remove() 372 | viewerRef.current = null; 373 | } 374 | }; 375 | }, [file]); 376 | 377 | const Direction = { X: 1, Y: 2, Z: 3 }; 378 | 379 | const setUpVectorZ = () => { 380 | if (viewerRef.current) { 381 | viewerRef.current 382 | .GetViewer() 383 | .SetUpVector( 384 | Direction.Z, 385 | viewerRef.current.GetViewer().GetCamera() 386 | ); 387 | } 388 | }; 389 | const setUpVectorY = () => { 390 | if (viewerRef.current) { 391 | viewerRef.current 392 | .GetViewer() 393 | .SetUpVector( 394 | Direction.Y, 395 | viewerRef.current.GetViewer().GetCamera() 396 | ); 397 | } 398 | }; 399 | 400 | const getSurfaceArea = () => { 401 | if (viewerRef.current) { 402 | let boundingBox = viewerRef.current 403 | .GetViewer() 404 | .GetBoundingBox( 405 | () => viewerRef.current.GetViewer().geometry.mainObject 406 | ); 407 | const objSurfaceArea = OV.CalculateSurfaceArea( 408 | viewerRef.current.GetModel() 409 | ); 410 | setSurfaceArea(objSurfaceArea.toFixed(2)); 411 | } 412 | }; 413 | 414 | const getVolume = () => { 415 | if (viewerRef.current) { 416 | let boundingBox = viewerRef.current 417 | .GetViewer() 418 | .GetBoundingBox( 419 | () => viewerRef.current.GetViewer().geometry.mainObject 420 | ); 421 | const objVolume = OV.CalculateVolume(viewerRef.current.GetModel()); 422 | setVolume(objVolume.toFixed(2)); 423 | } 424 | }; 425 | 426 | const turnOnEdge = () => { 427 | viewerRef.current 428 | .GetViewer() 429 | .SetEdgeSettings(true, { r: 0, g: 0, b: 0 }, 0.3); 430 | }; 431 | 432 | return ( 433 | <> 434 |
435 |
getVolume()} 438 | > 439 | Calculate Volume 440 |
441 |
getSurfaceArea()} 444 | > 445 | Calculate Surface Area 446 |
447 |
setUpVectorY()} 450 | > 451 | Set Vector Y 452 |
453 |
setUpVectorZ()} 456 | > 457 | Set Vector Z 458 |
459 |
turnOnEdge()} 462 | > 463 | Turn on Edge 464 |
465 |
466 |
472 |
473 | {volume && ( 474 |

475 | Volume: {volume}mm3 476 |

477 | )} 478 | {surfaceArea && ( 479 |

480 | Surface Area: {surfaceArea}mm2 481 |

482 | )} 483 |
484 | 485 | ); 486 | }; 487 | 488 | export default ViewerWithUI; 489 | 490 | ``` 491 | 492 | ## Tips and things to avoid 493 | 494 | - You must wait for the component to mount with 495 | useEffect before instantiating the viewer as the 496 | viewer calls window on initialisation which is only 497 | present in the browser, not node 498 | - The package isn't well documented so there are two 499 | ways to figure out what functions can be used: read 500 | the source code or log the Viewer object and examine 501 | in the browser 502 | - Instantiating the viewer object creates a canvas DOM 503 | element that we need to hold onto with React's 504 | useRef 505 | - Another important thing is that you need to 506 | correctly clean up the instantiated EmbeddedViewer. 507 | You can't do this by just using something like 508 | `viewerRef = null` in the useEffect cleanup function 509 | as the DOM element will be cleaned up but not all 510 | the references to the model and the arrays that hold 511 | the geometry data. As such, we need to explicitly 512 | remove these. The code I have at the moment was 513 | haphazardly slapped together and likely contains 514 | redundancy, if you come up with a better solution, 515 | please let me know but it does correctly reduce the 516 | memory usage 517 | - Even with the above code, the JS garbage collector 518 | is not the fastest so if you're running multiple 519 | instances of the viewer, be careful to stagger them 520 | if possible, as your user's browsers may run out of 521 | memory, particularly on lower end devices 522 | - Even if you somehow don't manage to run out of 523 | memory, you will likely run out of WebGL contexts 524 | (see 525 | https://github.com/kovacsv/Online3DViewer/issues/320) 526 | which is capped at anywhere between 8-12 depending 527 | on the device and the browser 528 | - If you need to pass a file around between components 529 | where the initial input may be lost, consider 530 | storing the file in state using something like 531 | Zustand 532 | -------------------------------------------------------------------------------- /use_as_react_component/components/Basic3DViewer.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import * as OV from "online-3d-viewer"; 3 | 4 | const Basic3DViewer = ({ file }) => { 5 | // The viewer is attached to parentDiv.current 6 | const parentDiv = useRef(null); 7 | // However, this really attaches the canvas element to the parentDiv, we need to also keep a reference for the viewer object itself which we'll do with viewerRef 8 | const viewerRef = useRef(null); 9 | 10 | useEffect(() => { 11 | // If there is a file passed in that isn't null, instantiate the viewer object 12 | if (file) { 13 | // Set the location of the libraries needed to load different models to lib, which in nextjs will point to "/public/libs" 14 | OV.SetExternalLibLocation("libs"); 15 | // Initialise all the 3D viewer elements 16 | OV.Init3DViewerElements(); 17 | // Before initialising the viewer, check that there isn't already a viewer object attached to viewerRef 18 | if (viewerRef.current === null) { 19 | // This is fairly self explanatory, initialise the viewer object with reasonable defaults. See the documentation for this component to see what other options you have 20 | let viewer = new OV.EmbeddedViewer(parentDiv.current, { 21 | camera: new OV.Camera( 22 | new OV.Coord3D(-150.0, 200.0, 300.0), 23 | new OV.Coord3D(0.0, 0.0, 0.0), 24 | new OV.Coord3D(0.0, 1.0, 0.0), 25 | 45.0 26 | ), 27 | backgroundColor: new OV.RGBAColor(255, 255, 255, 255), 28 | defaultColor: new OV.RGBColor(0, 100, 100), 29 | edgeSettings: { 30 | showEdges: false, 31 | edgeThreshold: 1, 32 | }, 33 | environmentMap: [ 34 | "../website/assets/envmaps/grayclouds/posx.jpg", 35 | "../website/assets/envmaps/grayclouds/negx.jpg", 36 | "../website/assets/envmaps/grayclouds/posy.jpg", 37 | "../website/assets/envmaps/grayclouds/negy.jpg", 38 | "../website/assets/envmaps/grayclouds/posz.jpg", 39 | "../website/assets/envmaps/grayclouds/negz.jpg", 40 | ], 41 | onModelLoaded: () => { 42 | console.log(viewerRef.current.GetViewer()); 43 | }, 44 | }); 45 | // ! This feels stupid but unfortunately, this resizing event can persist after clean up and lead to an error, one way to avoid this happening is to just overwrite the method so that it doesn't call this.viewer 46 | // ! Ideally, you'd clean it up during cleanup but I just found it easier to replace and ignore this event instead 47 | viewer.Resize = () => { 48 | console.log("I'm not resizing"); 49 | }; 50 | 51 | // Here, we assign the viewer object viewerRef.current to keep hold of it for later use 52 | viewerRef.current = viewer; 53 | 54 | // I've found This method of loading the file into the viewer works most reliably 55 | // Create a new data transfer object, add a File interface to it's items, grab the files and assign them to file_list and then load the model into the viewer with them 56 | var dt = new DataTransfer(); 57 | dt.items.add(file); 58 | var file_list = dt.files; 59 | viewer.LoadModelFromFileList(file_list); 60 | } 61 | } 62 | 63 | return () => { 64 | // ! We need to correctly clean up our viewer, it's listeners and related model data to ensure memory leaks don't occur 65 | // ! If you want to see what can happen if this isn't here, comment out this code and repeatedly spin up multiple viewers and then do a heap snapshot with chrome and you will see a massive amount of data that isn't being cleaned up by the garbage collector 66 | // We first check that both the viewerRef and parentDiv aren't null so we don't call a method on an object that doesn't exist 67 | if (viewerRef.current !== null && parentDiv.current !== null) { 68 | // ! I threw the kitchen sink at this to get rid of the memory leaks so some of this code is definitely redundant and there is likely a cleaner way of doing this 69 | // We delete the model, reset the state of the renderer and clear the viewer 70 | delete viewerRef.current.model; 71 | viewerRef.current.viewer.renderer.resetState(); 72 | viewerRef.current.viewer.Clear(); 73 | // Then we delete the whole viewer object 74 | delete viewerRef.current.viewer; 75 | // We grab canvas element before we delete it to ensure we lose the webgl context and it doesn't persist 76 | const gl = viewerRef.current.canvas.getContext("webgl2"); 77 | gl.getExtension("WEBGL_lose_context").loseContext(); 78 | // We replace the canvas element which will also replace all the event listeners which can cause the element and things associated with it to not be correctly cleaned up by the garbage collector 79 | const tempClone = viewerRef.current.canvas.cloneNode(true); 80 | viewerRef.current.canvas.parentNode.replaceChild( 81 | tempClone, 82 | viewerRef.current.canvas 83 | ); 84 | // Finally, we delete the canvas element and set the viewerRef.current to null, meaning everything should be properly cleaned up 85 | parentDiv.current.removeChild(parentDiv.current.children[0]); 86 | viewerRef.current = null; 87 | } 88 | }; 89 | }, [file]); 90 | 91 | return ( 92 | <> 93 |
99 | 100 | ); 101 | }; 102 | 103 | export default Basic3DViewer; 104 | -------------------------------------------------------------------------------- /use_as_react_component/components/ViewerWithUI.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import * as OV from "online-3d-viewer"; 3 | 4 | const ViewerWithUI = ({ file }) => { 5 | const parentDiv = useRef(null); 6 | const viewerRef = useRef(null); 7 | const [volume, setVolume] = useState(null); 8 | const [surfaceArea, setSurfaceArea] = useState(null); 9 | 10 | useEffect(() => { 11 | console.log(file); 12 | console.log(parentDiv); 13 | if (file) { 14 | OV.SetExternalLibLocation("libs"); 15 | OV.Init3DViewerElements(); 16 | // initialize the viewer with the parent element and some parameters 17 | if (viewerRef.current === null) { 18 | let viewer = new OV.EmbeddedViewer(parentDiv.current, { 19 | camera: new OV.Camera( 20 | new OV.Coord3D(-150.0, 200.0, 300.0), 21 | new OV.Coord3D(0.0, 0.0, 0.0), 22 | new OV.Coord3D(0.0, 1.0, 0.0), 23 | 45.0 24 | ), 25 | backgroundColor: new OV.RGBAColor(255, 255, 255, 255), 26 | defaultColor: new OV.RGBColor(0, 100, 100), 27 | edgeSettings: { 28 | showEdges: false, 29 | edgeThreshold: 1, 30 | }, 31 | environmentMap: [ 32 | "../website/assets/envmaps/grayclouds/posx.jpg", 33 | "../website/assets/envmaps/grayclouds/negx.jpg", 34 | "../website/assets/envmaps/grayclouds/posy.jpg", 35 | "../website/assets/envmaps/grayclouds/negy.jpg", 36 | "../website/assets/envmaps/grayclouds/posz.jpg", 37 | "../website/assets/envmaps/grayclouds/negz.jpg", 38 | ], 39 | onModelLoaded: () => { 40 | console.log(viewerRef.current.GetViewer()); 41 | }, 42 | }); 43 | // ! This feels stupid but unfortunately, this resizing event can persist after clean up and lead to an error, one way to avoid this happening is to just overwrite the method so that it doesn't call this.viewer 44 | viewer.Resize = () => { 45 | console.log("I'm not resizing"); 46 | }; 47 | viewerRef.current = viewer; 48 | var dt = new DataTransfer(); 49 | dt.items.add(file); 50 | var file_list = dt.files; 51 | viewer.LoadModelFromFileList(file_list); 52 | } 53 | } 54 | 55 | return () => { 56 | if (viewerRef.current !== null && parentDiv.current !== null) { 57 | delete viewerRef.current.model; 58 | viewerRef.current.viewer.renderer.resetState(); 59 | viewerRef.current.viewer.Clear(); 60 | delete viewerRef.current.viewer; 61 | const gl = viewerRef.current.canvas.getContext("webgl2"); 62 | gl.getExtension("WEBGL_lose_context").loseContext(); 63 | const tempClone = viewerRef.current.canvas.cloneNode(true); 64 | viewerRef.current.canvas.parentNode.replaceChild( 65 | tempClone, 66 | viewerRef.current.canvas 67 | ); 68 | parentDiv.current.removeChild(parentDiv.current.children[0]); 69 | // viewerRef.current.canvas.parentNode.removeChild(viewerRef.current.canvas); 70 | // viewerRef.current.canvas.remove() 71 | viewerRef.current = null; 72 | } 73 | }; 74 | }, [file]); 75 | 76 | const Direction = { X: 1, Y: 2, Z: 3 }; 77 | 78 | const setUpVectorZ = () => { 79 | if (viewerRef.current) { 80 | viewerRef.current 81 | .GetViewer() 82 | .SetUpVector( 83 | Direction.Z, 84 | viewerRef.current.GetViewer().GetCamera() 85 | ); 86 | } 87 | }; 88 | const setUpVectorY = () => { 89 | if (viewerRef.current) { 90 | viewerRef.current 91 | .GetViewer() 92 | .SetUpVector( 93 | Direction.Y, 94 | viewerRef.current.GetViewer().GetCamera() 95 | ); 96 | } 97 | }; 98 | 99 | const getSurfaceArea = () => { 100 | if (viewerRef.current) { 101 | let boundingBox = viewerRef.current 102 | .GetViewer() 103 | .GetBoundingBox( 104 | () => viewerRef.current.GetViewer().geometry.mainObject 105 | ); 106 | const objSurfaceArea = OV.CalculateSurfaceArea( 107 | viewerRef.current.GetModel() 108 | ); 109 | setSurfaceArea(objSurfaceArea.toFixed(2)); 110 | } 111 | }; 112 | 113 | const getVolume = () => { 114 | if (viewerRef.current) { 115 | let boundingBox = viewerRef.current 116 | .GetViewer() 117 | .GetBoundingBox( 118 | () => viewerRef.current.GetViewer().geometry.mainObject 119 | ); 120 | const objVolume = OV.CalculateVolume(viewerRef.current.GetModel()); 121 | setVolume(objVolume.toFixed(2)); 122 | } 123 | }; 124 | 125 | const turnOnEdge = () => { 126 | viewerRef.current 127 | .GetViewer() 128 | .SetEdgeSettings(true, { r: 0, g: 0, b: 0 }, 0.3); 129 | }; 130 | 131 | return ( 132 | <> 133 |
134 |
getVolume()} 137 | > 138 | Calculate Volume 139 |
140 |
getSurfaceArea()} 143 | > 144 | Calculate Surface Area 145 |
146 |
setUpVectorY()} 149 | > 150 | Set Vector Y 151 |
152 |
setUpVectorZ()} 155 | > 156 | Set Vector Z 157 |
158 |
turnOnEdge()} 161 | > 162 | Turn on Edge 163 |
164 |
165 |
171 |
172 | {volume && ( 173 |

174 | Volume: {volume}mm3 175 |

176 | )} 177 | {surfaceArea && ( 178 |

179 | Surface Area: {surfaceArea}mm2 180 |

181 | )} 182 |
183 | 184 | ); 185 | }; 186 | 187 | export default ViewerWithUI; 188 | -------------------------------------------------------------------------------- /use_as_react_component/components/ViewerWithUrls.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import * as OV from "online-3d-viewer"; 3 | 4 | const ViewerWithUrls = ({ url, loadModel }) => { 5 | const parentDiv = useRef(null); 6 | const viewerRef = useRef(null); 7 | 8 | useEffect(() => { 9 | if (url && loadModel) { 10 | OV.SetExternalLibLocation("libs"); 11 | OV.Init3DViewerElements(); 12 | if (viewerRef.current === null) { 13 | let viewer = new OV.EmbeddedViewer(parentDiv.current, { 14 | camera: new OV.Camera( 15 | new OV.Coord3D(-150.0, 200.0, 300.0), 16 | new OV.Coord3D(0.0, 0.0, 0.0), 17 | new OV.Coord3D(0.0, 1.0, 0.0), 18 | 45.0 19 | ), 20 | backgroundColor: new OV.RGBAColor(255, 255, 255, 255), 21 | defaultColor: new OV.RGBColor(0, 100, 100), 22 | edgeSettings: { 23 | showEdges: false, 24 | edgeThreshold: 1, 25 | }, 26 | environmentMap: [ 27 | "../website/assets/envmaps/grayclouds/posx.jpg", 28 | "../website/assets/envmaps/grayclouds/negx.jpg", 29 | "../website/assets/envmaps/grayclouds/posy.jpg", 30 | "../website/assets/envmaps/grayclouds/negy.jpg", 31 | "../website/assets/envmaps/grayclouds/posz.jpg", 32 | "../website/assets/envmaps/grayclouds/negz.jpg", 33 | ], 34 | onModelLoaded: () => { 35 | console.log(viewerRef.current.GetViewer()); 36 | }, 37 | }); 38 | viewer.Resize = () => { 39 | console.log("I'm not resizing"); 40 | }; 41 | 42 | // To load a file into the viewer using the url, we first pass a file name, OV.FileSource.Url and then the url of the model to the OV.InputFile constructor, put the newly created object in an array and save it as inputFiles 43 | let inputFiles = [ 44 | new OV.InputFile("test.stl", OV.FileSource.Url, url), 45 | ]; 46 | 47 | viewerRef.current = viewer; 48 | 49 | // Then we just pass inputFiles into the below method and viola 50 | viewer.LoadModelFromInputFiles(inputFiles); 51 | } 52 | } 53 | 54 | return () => { 55 | if (viewerRef.current !== null && parentDiv.current !== null) { 56 | delete viewerRef.current.model; 57 | viewerRef.current.viewer.renderer.resetState(); 58 | viewerRef.current.viewer.Clear(); 59 | delete viewerRef.current.viewer; 60 | const gl = viewerRef.current.canvas.getContext("webgl2"); 61 | gl.getExtension("WEBGL_lose_context").loseContext(); 62 | const tempClone = viewerRef.current.canvas.cloneNode(true); 63 | viewerRef.current.canvas.parentNode.replaceChild( 64 | tempClone, 65 | viewerRef.current.canvas 66 | ); 67 | parentDiv.current.removeChild(parentDiv.current.children[0]); 68 | // viewerRef.current.canvas.parentNode.removeChild(viewerRef.current.canvas); 69 | // viewerRef.current.canvas.remove() 70 | viewerRef.current = null; 71 | } 72 | }; 73 | }, [url, loadModel]); 74 | 75 | return ( 76 | <> 77 |
83 | 84 | ); 85 | }; 86 | 87 | export default ViewerWithUrls; 88 | -------------------------------------------------------------------------------- /use_as_react_component/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /use_as_react_component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "online-3d-viewer-nextjs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "eslint": "8.26.0", 13 | "eslint-config-next": "13.0.1", 14 | "next": "13.0.1", 15 | "online-3d-viewer": "^0.8.18", 16 | "prettier": "^2.7.1", 17 | "react": "18.2.0", 18 | "react-dom": "18.2.0" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^10.4.13", 22 | "postcss": "^8.4.18", 23 | "tailwindcss": "^3.2.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /use_as_react_component/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /use_as_react_component/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import Basic3DViewer from "../components/Basic3DViewer"; 3 | import ViewerWithUrls from "../components/ViewerWithUrls"; 4 | import ViewerWithUI from "../components/ViewerWithUI"; 5 | import { useEffect, useRef, useState } from "react"; 6 | 7 | export default function Home() { 8 | const fileInputRef = useRef(null); 9 | const fileInput2Ref = useRef(null); 10 | const [file, setFile] = useState(null); 11 | const [url, setUrl] = useState("http://localhost:3000/bunny.stl"); 12 | const [loadModel, setLoadModel] = useState(false); 13 | const [file2, setFile2] = useState(null); 14 | 15 | const fileSelected = (e) => { 16 | e.preventDefault(); 17 | if (fileInputRef.current.files.length) { 18 | setFile(fileInputRef.current.files[0]); 19 | } 20 | }; 21 | 22 | const fileSelected2 = (e) => { 23 | e.preventDefault(); 24 | if (fileInput2Ref.current.files.length) { 25 | setFile2(fileInput2Ref.current.files[0]); 26 | } 27 | }; 28 | 29 | return ( 30 |
31 | 32 | Online 3D Viewer with Next.js 33 | 37 | 38 | 39 | 40 |
41 |

42 | Online 3D Viewer with Next.js 43 |

44 |
45 |

46 | This is a work in progress! If you want to add or 47 | correct these examples, please submit a pull request! 48 |

49 |

50 | There was a lack of documentation for Online 3D Viewer 51 | and how to use it with React and Next.js, so I've made 52 | this guide to help others encountering the same issue. 53 | I'll show some examples of how you can use the Online 3D 54 | Viewer and while also explaining some errors and issues 55 | I encountered and how to resolve them. This is very much 56 | a work in progress and isn't perfect, if you have any 57 | feedback or additional advice, please submit a pull 58 | request 59 |

60 |

61 | To setup this project, clone the repo, npm install the 62 | packages and run the server with npm run dev. I've only 63 | tested this with stl and 3dm files, other files may 64 | produce unique issues, if you encounter them, please let 65 | me know 66 |

67 |
68 |
69 |

70 | Setting up Online 3D Viewer with your Next app: 71 |

72 |

73 | First, install the npm package from 74 | https://www.npmjs.com/package/online-3d-viewer. Import 75 | the package into your project with `import * as OV from 76 | "online-3d-viewer"` 77 |

78 |

79 | Secondly, on order for this package to work in your 80 | project, make sure you have the libs folder in your 81 | public folder, as shown in this repo. These are the 82 | external packages used by Online-3D-Viewer to load the 83 | different types of 3D file client side.{" "} 84 | 85 | THE PACKAGE WILL NOT WORK WITHOUT THESE PRESENT. 86 | 87 |

88 |

89 | This is all the setup you'll need and we'll now look at 90 | the different ways you can use the viewer and load in 91 | files 92 |

93 |
94 |
95 |

Example 1: A basic 3D viewer

96 |

97 | This is a very basic example of how you can have a file 98 | input, upload a file and have it display in the 3D 99 | viewer. This example will also clean up correctly, 100 | avoiding memory leaks that can happen while using this 101 | package with React . Using this example's component, you 102 | can easily spin up multiple different viewers at the 103 | same time and passing different files into each one, 104 | though I would be careful doing so for reasons mentioned 105 | at the bottom of this page. 106 |

107 |

108 | See the "Basic3DViewer" component in the components 109 | folder to see how this was constructed. I've left 110 | annotated notes explaining how everything works but will 111 | add some additional comments here 112 |

113 |

114 | {" "} 115 | You may notice that the method of loading files seems 116 | over complicated. As the viewer loads files in using a 117 | FileList, you don't need to extract the file, put it in 118 | state, then use a DataTransfer object to recreate a 119 | FileList. However, this method works better if you're 120 | holding File Interfaces in state, e.g. with Zustand, and 121 | passing them between components, as this way the 122 | original file input DOM element doesn't need to be on 123 | the page. This is useful using UI patterns like Wizards, 124 | where a user may upload a file on one step and then the 125 | app may need the file again on another step despite the 126 | fact that DOM input is gone 127 |

128 |
129 |
130 | 136 | 137 |
138 |
139 |

140 | Example 2: Different methods to load files 141 |

142 |

143 | There are two methods that I've used to load files into 144 | the viewer: LoadModelFromFileList and 145 | LoadModelFromInputFiles. If you need to load a file 146 | uploaded through a DOM input by the user, use 147 | LoadModelFromFileList and use the prior example. If you 148 | want to load the file from a URL, use the example below. 149 |

150 |

151 | As with the prior example, please see the 152 | "ViewerWithUrls" component and see the annotated code. 153 |

154 |
155 |
156 |
157 |
158 | 159 | setUrl(e.target.value)} 163 | className=" border-2 border-black rounded-sm" 164 | /> 165 |
166 | {!loadModel && ( 167 | 170 | )} 171 | {loadModel && ( 172 | 175 | )} 176 |
177 | 178 |
179 |
180 |

181 | Example 3: Using viewer methods with your own UI 182 |

183 |

184 | Obviously, most users will likely want to use this 185 | package for more than just viewing 3D files. 186 | Unfortunately, there isn't any real documentation on how 187 | to do so and what can be done. I'll show some examples 188 | here but until there is more documentation, I'd advise 189 | you to read the code, particularly 190 | https://github.com/kovacsv/Online3DViewer/blob/master/source/engine/viewer/viewer.js 191 | and 192 | https://github.com/kovacsv/Online3DViewer/blob/master/source/engine/viewer/embeddedviewer.js 193 | to see what's possible 194 |

195 |

196 | I haven't commented the code for this component, it's 197 | fairly self explanatory. To better understand why 198 | certain parameters are being passed into each function, 199 | see the source code linked above 200 |

201 |
202 |
203 | 209 | 210 |
211 | 212 |
213 |

Tips and things to avoid

214 |
    215 |
  • 216 | You must wait for the component to mount with 217 | useEffect before instantiating the viewer as the 218 | viewer calls window on initialisation which is only 219 | present in the browser, not node 220 |
  • 221 |
  • 222 | The package isn't well documented so there are two 223 | ways to figure out what functions can be used: read 224 | the source code or log the Viewer object and examine 225 | in the browser 226 |
  • 227 |
  • 228 | Instantiating the viewer object creates a canvas DOM 229 | element that we need to hold onto with React's 230 | useRef 231 |
  • 232 |
  • 233 | Another important thing is that you need to 234 | correctly clean up the instantiated EmbeddedViewer. 235 | You can't do this by just using something like 236 | `viewerRef = null` in the useEffect cleanup function 237 | as the DOM element will be cleaned up but not all 238 | the references to the model and the arrays that hold 239 | the geometry data. As such, we need to explicitly 240 | remove these. The code I have at the moment was 241 | haphazardly slapped together and likely contains 242 | redundancy, if you come up with a better solution, 243 | please let me know but it does correctly reduce the 244 | memory usage 245 |
  • 246 |
  • 247 | Even with the above code, the JS garbage collector 248 | is not the fastest so if you're running multiple 249 | instances of the viewer, be careful to stagger them 250 | if possible, as your user's browsers may run out of 251 | memory, particularly on lower end devices 252 |
  • 253 |
  • 254 | Even if you somehow don't manage to run out of 255 | memory, you will likely run out of WebGL contexts 256 | (see 257 | https://github.com/kovacsv/Online3DViewer/issues/320) 258 | which is capped at anywhere between 8-12 depending 259 | on the device and the browser 260 |
  • 261 |
  • 262 | If you need to pass a file around between components 263 | where the initial input may be lost, consider 264 | storing the file in state using something like 265 | Zustand 266 |
  • 267 |
268 |
269 |
270 |
271 | ); 272 | } 273 | -------------------------------------------------------------------------------- /use_as_react_component/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /use_as_react_component/public/bunny.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovacsv/Online3DViewerExamples/02b487143b5e295ea91d26ef7bd956b1197aa690/use_as_react_component/public/bunny.stl -------------------------------------------------------------------------------- /use_as_react_component/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovacsv/Online3DViewerExamples/02b487143b5e295ea91d26ef7bd956b1197aa690/use_as_react_component/public/favicon.ico -------------------------------------------------------------------------------- /use_as_react_component/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /use_as_react_component/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 4rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | } 52 | 53 | .description { 54 | margin: 4rem 0; 55 | line-height: 1.5; 56 | font-size: 1.5rem; 57 | } 58 | 59 | .code { 60 | background: #fafafa; 61 | border-radius: 5px; 62 | padding: 0.75rem; 63 | font-size: 1.1rem; 64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 65 | Bitstream Vera Sans Mono, Courier New, monospace; 66 | } 67 | 68 | .grid { 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-wrap: wrap; 73 | max-width: 800px; 74 | } 75 | 76 | .card { 77 | margin: 1rem; 78 | padding: 1.5rem; 79 | text-align: left; 80 | color: inherit; 81 | text-decoration: none; 82 | border: 1px solid #eaeaea; 83 | border-radius: 10px; 84 | transition: color 0.15s ease, border-color 0.15s ease; 85 | max-width: 300px; 86 | } 87 | 88 | .card:hover, 89 | .card:focus, 90 | .card:active { 91 | color: #0070f3; 92 | border-color: #0070f3; 93 | } 94 | 95 | .card h2 { 96 | margin: 0 0 1rem 0; 97 | font-size: 1.5rem; 98 | } 99 | 100 | .card p { 101 | margin: 0; 102 | font-size: 1.25rem; 103 | line-height: 1.5; 104 | } 105 | 106 | .logo { 107 | height: 1em; 108 | margin-left: 0.5rem; 109 | } 110 | 111 | @media (max-width: 600px) { 112 | .grid { 113 | width: 100%; 114 | flex-direction: column; 115 | } 116 | } 117 | 118 | @media (prefers-color-scheme: dark) { 119 | .card, 120 | .footer { 121 | border-color: #222; 122 | } 123 | .code { 124 | background: #111; 125 | } 126 | .logo img { 127 | filter: invert(1); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /use_as_react_component/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | -------------------------------------------------------------------------------- /use_as_react_component/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./app/**/*.{js,ts,jsx,tsx}", 5 | "./pages/**/*.{js,ts,jsx,tsx}", 6 | "./components/**/*.{js,ts,jsx,tsx}", 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } --------------------------------------------------------------------------------