├── .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 | [](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 | 
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 | 
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 | Enter URL here:
159 | setUrl(e.target.value)}
163 | className=" border-2 border-black rounded-sm"
164 | />
165 |
166 | {!loadModel && (
167 |
setLoadModel(true)}>
168 | Load Model from URL
169 |
170 | )}
171 | {loadModel && (
172 |
setLoadModel(false)}>
173 | Remove Model
174 |
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 | }
--------------------------------------------------------------------------------