init
							
								
								
									
										4
									
								
								.browserslistrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					> 1%
 | 
				
			||||||
 | 
					last 2 versions
 | 
				
			||||||
 | 
					not dead
 | 
				
			||||||
 | 
					not ie 11
 | 
				
			||||||
							
								
								
									
										6
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
 | 
				
			||||||
 | 
					charset = utf-8
 | 
				
			||||||
 | 
					indent_size = 2
 | 
				
			||||||
 | 
					indent_style = space
 | 
				
			||||||
 | 
					insert_final_newline = true
 | 
				
			||||||
 | 
					trim_trailing_whitespace = true
 | 
				
			||||||
							
								
								
									
										6
									
								
								.env.development
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					ENV = 'production'
 | 
				
			||||||
 | 
					VUE_APP_MODE = 'production'
 | 
				
			||||||
 | 
					VITE_BASE_URL = 'https://wly-demo.wlyun.co'
 | 
				
			||||||
 | 
					VITE_ENVURL='https://wlyun-public.oss-cn-hangzhou.aliyuncs.com/shuangxi/hdr/'
 | 
				
			||||||
 | 
					VITE_MODELURL='https://wlyun-public.oss-cn-hangzhou.aliyuncs.com/shuangxi/model/'
 | 
				
			||||||
 | 
					VITE_PUBLIC_URL=/
 | 
				
			||||||
							
								
								
									
										6
									
								
								.env.production
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					ENV = 'production'
 | 
				
			||||||
 | 
					VUE_APP_MODE = 'production'
 | 
				
			||||||
 | 
					VITE_BASE_URL = 'https://wly-demo.wlyun.co'
 | 
				
			||||||
 | 
					VITE_ENVURL='https://wlyun-public.oss-cn-hangzhou.aliyuncs.com/shuangxi/hdr/'
 | 
				
			||||||
 | 
					VITE_MODELURL='https://wlyun-public.oss-cn-hangzhou.aliyuncs.com/shuangxi/model/'
 | 
				
			||||||
 | 
					VITE_PUBLIC_URL=./
 | 
				
			||||||
							
								
								
									
										22
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					/dist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# local env files
 | 
				
			||||||
 | 
					.env.local
 | 
				
			||||||
 | 
					.env.*.local
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Log files
 | 
				
			||||||
 | 
					npm-debug.log*
 | 
				
			||||||
 | 
					yarn-debug.log*
 | 
				
			||||||
 | 
					yarn-error.log*
 | 
				
			||||||
 | 
					pnpm-debug.log*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Editor directories and files
 | 
				
			||||||
 | 
					.idea
 | 
				
			||||||
 | 
					.vscode
 | 
				
			||||||
 | 
					*.suo
 | 
				
			||||||
 | 
					*.ntvs*
 | 
				
			||||||
 | 
					*.njsproj
 | 
				
			||||||
 | 
					*.sln
 | 
				
			||||||
 | 
					*.sw?
 | 
				
			||||||
							
								
								
									
										954
									
								
								bun.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,954 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "lockfileVersion": 1,
 | 
				
			||||||
 | 
					  "workspaces": {
 | 
				
			||||||
 | 
					    "": {
 | 
				
			||||||
 | 
					      "name": "client",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@babylonjs/core": "^8.25.0",
 | 
				
			||||||
 | 
					        "@babylonjs/loaders": "^8.25.0",
 | 
				
			||||||
 | 
					        "@fontsource/roboto": "5.2.6",
 | 
				
			||||||
 | 
					        "@mdi/font": "7.4.47",
 | 
				
			||||||
 | 
					        "@types/babylonjs": "^2.4.5",
 | 
				
			||||||
 | 
					        "pinia": "^3.0.3",
 | 
				
			||||||
 | 
					        "popmotion": "^11.0.5",
 | 
				
			||||||
 | 
					        "vue": "^3.5.17",
 | 
				
			||||||
 | 
					        "vuetify": "^3.9.1",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "devDependencies": {
 | 
				
			||||||
 | 
					        "@tsconfig/node22": "^22.0.0",
 | 
				
			||||||
 | 
					        "@types/node": "^22.9.0",
 | 
				
			||||||
 | 
					        "@vitejs/plugin-vue": "^5.2.3",
 | 
				
			||||||
 | 
					        "@vue/tsconfig": "^0.7.0",
 | 
				
			||||||
 | 
					        "eslint": "^9.30.1",
 | 
				
			||||||
 | 
					        "eslint-config-vuetify": "^4.0.0",
 | 
				
			||||||
 | 
					        "npm-run-all2": "^7.0.2",
 | 
				
			||||||
 | 
					        "sass-embedded": "^1.89.2",
 | 
				
			||||||
 | 
					        "typescript": "~5.8.3",
 | 
				
			||||||
 | 
					        "unplugin-fonts": "^1.3.1",
 | 
				
			||||||
 | 
					        "unplugin-vue-components": "^28.8.0",
 | 
				
			||||||
 | 
					        "unplugin-vue-router": "^0.14.0",
 | 
				
			||||||
 | 
					        "vite": "^6.3.5",
 | 
				
			||||||
 | 
					        "vite-plugin-vuetify": "^2.1.1",
 | 
				
			||||||
 | 
					        "vue-router": "^4.5.1",
 | 
				
			||||||
 | 
					        "vue-tsc": "^3.0.1",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "packages": {
 | 
				
			||||||
 | 
					    "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@14.2.0", "https://registry.npmmirror.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.2.0.tgz", { "dependencies": { "js-yaml": "^4.1.0" }, "peerDependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-NaGMMWwppbByagq+LwQMq6PMXHFWVu6kSwwx+eJfYTJ5zdpOvb9TIk6ZWxEEeXMUvGdVOZq3JalYsjsTZDvtkA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@babel/parser": ["@babel/parser@7.28.3", "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.3.tgz", { "dependencies": { "@babel/types": "^7.28.2" }, "bin": "./bin/babel-parser.js" }, "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@babel/types": ["@babel/types@7.28.2", "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@babylonjs/core": ["@babylonjs/core@8.25.0", "https://registry.npmmirror.com/@babylonjs/core/-/core-8.25.0.tgz", {}, "sha512-hBPSp+hkcOd35Dg8eUh35nTrod+J8ayLw2f5n+nynQqVEDb54CptbVTeV6cd3cnrH65xSm+us/c+h1H6JzAbXw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@babylonjs/loaders": ["@babylonjs/loaders@8.25.0", "https://registry.npmmirror.com/@babylonjs/loaders/-/loaders-8.25.0.tgz", { "peerDependencies": { "@babylonjs/core": "^8.0.0", "babylonjs-gltf2interface": "^8.0.0" } }, "sha512-hoXE/AwOli7l+R3Nz7cmUsnt5cSrIRQWaGaUjmQ4dZJNI0JqzmkxUuQq9eJfNUtjDnjEM1RfXzIlt2QdFg4KoA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@bufbuild/protobuf": ["@bufbuild/protobuf@2.7.0", "https://registry.npmmirror.com/@bufbuild/protobuf/-/protobuf-2.7.0.tgz", {}, "sha512-qn6tAIZEw5i/wiESBF4nQxZkl86aY4KoO0IkUa2Lh+rya64oTOdJQFlZuMwI1Qz9VBJQrQC4QlSA2DNek5gCOA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@clack/core": ["@clack/core@0.5.0", "https://registry.npmmirror.com/@clack/core/-/core-0.5.0.tgz", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@clack/prompts": ["@clack/prompts@0.11.0", "https://registry.npmmirror.com/@clack/prompts/-/prompts-0.11.0.tgz", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@emnapi/core": ["@emnapi/core@1.5.0", "https://registry.npmmirror.com/@emnapi/core/-/core-1.5.0.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.5.0.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.9.tgz", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", { "os": "android", "cpu": "arm64" }, "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/android-x64": ["@esbuild/android-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.9.tgz", { "os": "android", "cpu": "x64" }, "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", { "os": "linux", "cpu": "arm" }, "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", { "os": "linux", "cpu": "none" }, "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", { "os": "linux", "cpu": "none" }, "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", { "os": "linux", "cpu": "none" }, "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", { "os": "linux", "cpu": "x64" }, "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", { "os": "none", "cpu": "arm64" }, "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", { "os": "none", "cpu": "x64" }, "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", { "os": "openbsd", "cpu": "arm64" }, "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", { "os": "none", "cpu": "arm64" }, "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.9", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.9", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/compat": ["@eslint/compat@1.3.2", "https://registry.npmmirror.com/@eslint/compat/-/compat-1.3.2.tgz", { "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/config-array": ["@eslint/config-array@0.21.0", "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.0.tgz", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/core": ["@eslint/core@0.15.2", "https://registry.npmmirror.com/@eslint/core/-/core-0.15.2.tgz", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/js": ["@eslint/js@9.34.0", "https://registry.npmmirror.com/@eslint/js/-/js-9.34.0.tgz", {}, "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.6.tgz", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@fontsource/roboto": ["@fontsource/roboto@5.2.6", "https://registry.npmmirror.com/@fontsource/roboto/-/roboto-5.2.6.tgz", {}, "sha512-hzarG7yAhMoP418smNgfY4fO7UmuUEm5JUtbxCoCcFHT0hOJB+d/qAEyoNjz7YkPU5OjM2LM8rJnW8hfm0JLaA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@humanfs/core": ["@humanfs/core@0.19.1", "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@humanfs/node": ["@humanfs/node@0.16.6", "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.6.tgz", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@mdi/font": ["@mdi/font@7.4.47", "https://registry.npmmirror.com/@mdi/font/-/font-7.4.47.tgz", {}, "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher": ["@parcel/watcher@2.5.1", "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@pkgr/core": ["@pkgr/core@0.2.9", "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.9.tgz", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz", { "os": "none", "cpu": "arm64" }, "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.0", "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@4.4.1", "https://registry.npmmirror.com/@stylistic/eslint-plugin/-/eslint-plugin-4.4.1.tgz", { "dependencies": { "@typescript-eslint/utils": "^8.32.1", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", "picomatch": "^4.0.2" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-CEigAk7eOLyHvdgmpZsKFwtiqS2wFwI1fn4j09IU9GmD4euFM4jEBAViWeCqaNLlbX2k2+A/Fq9cje4HQBXuJQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@tsconfig/node22": ["@tsconfig/node22@22.0.2", "https://registry.npmmirror.com/@tsconfig/node22/-/node22-22.0.2.tgz", {}, "sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@types/babylonjs": ["@types/babylonjs@2.4.5", "https://registry.npmmirror.com/@types/babylonjs/-/babylonjs-2.4.5.tgz", { "dependencies": { "babylonjs": "*" } }, "sha512-hHImUhEeDk8OAhhwXeEsDo3osfF+zTREhSOQgAoj5E0iJL2/bN2ojavSyYFdm7vXK8WNhf7i4YEF6B3ix/WL+Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@types/estree": ["@types/estree@1.0.8", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@types/json-schema": ["@types/json-schema@7.0.15", "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@types/node": ["@types/node@22.18.0", "https://registry.npmmirror.com/@types/node/-/node-22.18.0.tgz", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/type-utils": "8.41.0", "@typescript-eslint/utils": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.41.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/parser": ["@typescript-eslint/parser@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.41.0.tgz", { "dependencies": { "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.41.0", "@typescript-eslint/types": "^8.41.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0" } }, "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", { "dependencies": { "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/types": ["@typescript-eslint/types@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.41.0.tgz", {}, "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", { "dependencies": { "@typescript-eslint/project-service": "8.41.0", "@typescript-eslint/tsconfig-utils": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/visitor-keys": "8.41.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/utils": ["@typescript-eslint/utils@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.41.0.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.41.0", "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", { "dependencies": { "@typescript-eslint/types": "8.41.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "https://registry.npmmirror.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vitest/eslint-plugin": ["@vitest/eslint-plugin@1.3.5", "https://registry.npmmirror.com/@vitest/eslint-plugin/-/eslint-plugin-1.3.5.tgz", { "dependencies": { "@typescript-eslint/scope-manager": "^8.41.0", "@typescript-eslint/utils": "^8.24.1" }, "peerDependencies": { "eslint": ">= 8.57.0", "typescript": ">= 5.0.0", "vitest": "*" }, "optionalPeers": ["typescript", "vitest"] }, "sha512-vdQL1s+Yb9i7xXFur0qRpECwkafrp+L84EXppg3Xs+Iu+5M8smkh5I2rD5opD7cEaSBE3UEebB3xbDANqALpKA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@volar/language-core": ["@volar/language-core@2.4.23", "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.23.tgz", { "dependencies": { "@volar/source-map": "2.4.23" } }, "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@volar/source-map": ["@volar/source-map@2.4.23", "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.23.tgz", {}, "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@volar/typescript": ["@volar/typescript@2.4.23", "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.23.tgz", { "dependencies": { "@volar/language-core": "2.4.23", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue-macros/common": ["@vue-macros/common@3.0.0-beta.15", "https://registry.npmmirror.com/@vue-macros/common/-/common-3.0.0-beta.15.tgz", { "dependencies": { "@vue/compiler-sfc": "^3.5.17", "ast-kit": "^2.1.0", "local-pkg": "^1.1.1", "magic-string-ast": "^1.0.0", "unplugin-utils": "^0.2.4" }, "peerDependencies": { "vue": "^2.7.0 || ^3.2.25" }, "optionalPeers": ["vue"] }, "sha512-DMgq/rIh1H20WYNWU7krIbEfJRYDDhy7ix64GlT4AVUJZZWCZ5pxiYVJR3A3GmWQPkn7Pg7i3oIiGqu4JGC65w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/compiler-core": ["@vue/compiler-core@3.5.20", "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.20.tgz", { "dependencies": { "@babel/parser": "^7.28.3", "@vue/shared": "3.5.20", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-8TWXUyiqFd3GmP4JTX9hbiTFRwYHgVL/vr3cqhr4YQ258+9FADwvj7golk2sWNGHR67QgmCZ8gz80nQcMokhwg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/compiler-dom": ["@vue/compiler-dom@3.5.20", "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.20.tgz", { "dependencies": { "@vue/compiler-core": "3.5.20", "@vue/shared": "3.5.20" } }, "sha512-whB44M59XKjqUEYOMPYU0ijUV0G+4fdrHVKDe32abNdX/kJe1NUEMqsi4cwzXa9kyM9w5S8WqFsrfo1ogtBZGQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.20", "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.20.tgz", { "dependencies": { "@babel/parser": "^7.28.3", "@vue/compiler-core": "3.5.20", "@vue/compiler-dom": "3.5.20", "@vue/compiler-ssr": "3.5.20", "@vue/shared": "3.5.20", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-SFcxapQc0/feWiSBfkGsa1v4DOrnMAQSYuvDMpEaxbpH5dKbnEM5KobSNSgU+1MbHCl+9ftm7oQWxvwDB6iBfw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.20", "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.20.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.20", "@vue/shared": "3.5.20" } }, "sha512-RSl5XAMc5YFUXpDQi+UQDdVjH9FnEpLDHIALg5J0ITHxkEzJ8uQLlo7CIbjPYqmZtt6w0TsIPbo1izYXwDG7JA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/devtools-api": ["@vue/devtools-api@6.6.4", "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/devtools-kit": ["@vue/devtools-kit@7.7.7", "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", { "dependencies": { "@vue/devtools-shared": "^7.7.7", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/devtools-shared": ["@vue/devtools-shared@7.7.7", "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/language-core": ["@vue/language-core@3.0.6", "https://registry.npmmirror.com/@vue/language-core/-/language-core-3.0.6.tgz", { "dependencies": { "@volar/language-core": "2.4.23", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^2.0.5", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-e2RRzYWm+qGm8apUHW1wA5RQxzNhkqbbKdbKhiDUcmMrNAZGyM8aTiL3UrTqkaFI5s7wJRGGrp4u3jgusuBp2A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/reactivity": ["@vue/reactivity@3.5.20", "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.20.tgz", { "dependencies": { "@vue/shared": "3.5.20" } }, "sha512-hS8l8x4cl1fmZpSQX/NXlqWKARqEsNmfkwOIYqtR2F616NGfsLUm0G6FQBK6uDKUCVyi1YOL8Xmt/RkZcd/jYQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/runtime-core": ["@vue/runtime-core@3.5.20", "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.20.tgz", { "dependencies": { "@vue/reactivity": "3.5.20", "@vue/shared": "3.5.20" } }, "sha512-vyQRiH5uSZlOa+4I/t4Qw/SsD/gbth0SW2J7oMeVlMFMAmsG1rwDD6ok0VMmjXY3eI0iHNSSOBilEDW98PLRKw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/runtime-dom": ["@vue/runtime-dom@3.5.20", "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.20.tgz", { "dependencies": { "@vue/reactivity": "3.5.20", "@vue/runtime-core": "3.5.20", "@vue/shared": "3.5.20", "csstype": "^3.1.3" } }, "sha512-KBHzPld/Djw3im0CQ7tGCpgRedryIn4CcAl047EhFTCCPT2xFf4e8j6WeKLgEEoqPSl9TYqShc3Q6tpWpz/Xgw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/server-renderer": ["@vue/server-renderer@3.5.20", "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.20.tgz", { "dependencies": { "@vue/compiler-ssr": "3.5.20", "@vue/shared": "3.5.20" }, "peerDependencies": { "vue": "3.5.20" } }, "sha512-HthAS0lZJDH21HFJBVNTtx+ULcIbJQRpjSVomVjfyPkFSpCwvsPTA+jIzOaUm3Hrqx36ozBHePztQFg6pj5aKg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/shared": ["@vue/shared@3.5.20", "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.20.tgz", {}, "sha512-SoRGP596KU/ig6TfgkCMbXkr4YJ91n/QSdMuqeP5r3hVIYA3CPHUBCc7Skak0EAKV+5lL4KyIh61VA/pK1CIAA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vue/tsconfig": ["@vue/tsconfig@0.7.0", "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.7.0.tgz", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@vuetify/loader-shared": ["@vuetify/loader-shared@2.1.1", "https://registry.npmmirror.com/@vuetify/loader-shared/-/loader-shared-2.1.1.tgz", { "dependencies": { "upath": "^2.0.1" }, "peerDependencies": { "vue": "^3.0.0", "vuetify": "^3.0.0" } }, "sha512-jSZTzTYaoiv8iwonFCVZQ0YYX/M+Uyl4ng+C4egMJT0Hcmh9gIxJL89qfZICDeo3g0IhqrvipW2FFKKRDMtVcA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "acorn": ["acorn@8.15.0", "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "acorn-jsx": ["acorn-jsx@5.3.2", "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ajv": ["ajv@6.12.6", "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "alien-signals": ["alien-signals@2.0.7", "https://registry.npmmirror.com/alien-signals/-/alien-signals-2.0.7.tgz", {}, "sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ansi-styles": ["ansi-styles@6.2.1", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "anymatch": ["anymatch@3.1.3", "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "argparse": ["argparse@2.0.1", "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "aria-query": ["aria-query@5.3.2", "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ast-kit": ["ast-kit@2.1.2", "https://registry.npmmirror.com/ast-kit/-/ast-kit-2.1.2.tgz", { "dependencies": { "@babel/parser": "^7.28.0", "pathe": "^2.0.3" } }, "sha512-cl76xfBQM6pztbrFWRnxbrDm9EOqDr1BF6+qQnnDZG2Co2LjyUktkN9GTJfBAfdae+DbT2nJf2nCGAdDDN7W2g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ast-walker-scope": ["ast-walker-scope@0.8.2", "https://registry.npmmirror.com/ast-walker-scope/-/ast-walker-scope-0.8.2.tgz", { "dependencies": { "@babel/parser": "^7.28.3", "ast-kit": "^2.1.2" } }, "sha512-3pYeLyDZ6nJew9QeBhS4Nly02269Dkdk32+zdbbKmL6n4ZuaGorwwA+xx12xgOciA8BF1w9x+dlH7oUkFTW91w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "babylonjs": ["babylonjs@8.25.0", "https://registry.npmmirror.com/babylonjs/-/babylonjs-8.25.0.tgz", {}, "sha512-L1fhY6M50cnHU43b1THT5ESs1/tMGIqLUi8VnZrSqblu+RComoRUMXLCpkW/14lT6bkNG/dTmGp4IB2n2/FRLA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "babylonjs-gltf2interface": ["babylonjs-gltf2interface@8.25.0", "https://registry.npmmirror.com/babylonjs-gltf2interface/-/babylonjs-gltf2interface-8.25.0.tgz", {}, "sha512-yB0T/TWBUmHKvEtpELoMXZJrmaPciZzsfbbUfj0nLKcZ+TkgLq/dy0Nb/+O2u05dWZRwAC+4lfv1CdeEfrUk0g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "balanced-match": ["balanced-match@1.0.2", "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "binary-extensions": ["binary-extensions@2.3.0", "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "birpc": ["birpc@2.5.0", "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz", {}, "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "boolbase": ["boolbase@1.0.0", "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "brace-expansion": ["brace-expansion@1.1.12", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "braces": ["braces@3.0.3", "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "browserslist": ["browserslist@4.25.4", "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.4.tgz", { "dependencies": { "caniuse-lite": "^1.0.30001737", "electron-to-chromium": "^1.5.211", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "buffer-builder": ["buffer-builder@0.2.0", "https://registry.npmmirror.com/buffer-builder/-/buffer-builder-0.2.0.tgz", {}, "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "builtin-modules": ["builtin-modules@5.0.0", "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-5.0.0.tgz", {}, "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "callsites": ["callsites@3.1.0", "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "caniuse-lite": ["caniuse-lite@1.0.30001739", "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz", {}, "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "chalk": ["chalk@4.1.2", "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "change-case": ["change-case@5.4.4", "https://registry.npmmirror.com/change-case/-/change-case-5.4.4.tgz", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "chokidar": ["chokidar@3.6.0", "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ci-info": ["ci-info@4.3.0", "https://registry.npmmirror.com/ci-info/-/ci-info-4.3.0.tgz", {}, "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "citty": ["citty@0.1.6", "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "clean-regexp": ["clean-regexp@1.0.0", "https://registry.npmmirror.com/clean-regexp/-/clean-regexp-1.0.0.tgz", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "color-convert": ["color-convert@2.0.1", "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "color-name": ["color-name@1.1.4", "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "colorjs.io": ["colorjs.io@0.5.2", "https://registry.npmmirror.com/colorjs.io/-/colorjs.io-0.5.2.tgz", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "comment-parser": ["comment-parser@1.4.1", "https://registry.npmmirror.com/comment-parser/-/comment-parser-1.4.1.tgz", {}, "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "concat-map": ["concat-map@0.0.1", "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "confbox": ["confbox@0.2.2", "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "consola": ["consola@3.4.2", "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "copy-anything": ["copy-anything@3.0.5", "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "core-js-compat": ["core-js-compat@3.45.1", "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.45.1.tgz", { "dependencies": { "browserslist": "^4.25.3" } }, "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "cross-spawn": ["cross-spawn@7.0.6", "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "cssesc": ["cssesc@3.0.0", "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "csstype": ["csstype@3.1.3", "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "de-indent": ["de-indent@1.0.2", "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "debug": ["debug@4.4.1", "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "deep-is": ["deep-is@0.1.4", "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "detect-libc": ["detect-libc@1.0.3", "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "electron-to-chromium": ["electron-to-chromium@1.5.211", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz", {}, "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "emoji-regex": ["emoji-regex@10.5.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.5.0.tgz", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "entities": ["entities@4.5.0", "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "esbuild": ["esbuild@0.25.9", "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.9.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "escalade": ["escalade@3.2.0", "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "escape-string-regexp": ["escape-string-regexp@4.0.0", "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint": ["eslint@9.34.0", "https://registry.npmmirror.com/eslint/-/eslint-9.34.0.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-compat-utils": ["eslint-compat-utils@0.6.5", "https://registry.npmmirror.com/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-config-flat-gitignore": ["eslint-config-flat-gitignore@2.1.0", "https://registry.npmmirror.com/eslint-config-flat-gitignore/-/eslint-config-flat-gitignore-2.1.0.tgz", { "dependencies": { "@eslint/compat": "^1.2.5" }, "peerDependencies": { "eslint": "^9.5.0" } }, "sha512-cJzNJ7L+psWp5mXM7jBX+fjHtBvvh06RBlcweMhKD8jWqQw0G78hOW5tpVALGHGFPsBV+ot2H+pdDGJy6CV8pA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-config-vuetify": ["eslint-config-vuetify@4.1.0", "https://registry.npmmirror.com/eslint-config-vuetify/-/eslint-config-vuetify-4.1.0.tgz", { "dependencies": { "@clack/prompts": "^0.11.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.28.0", "@stylistic/eslint-plugin": "^4.4.0", "@typescript-eslint/parser": "^8.33.0", "eslint-config-flat-gitignore": "^2.1.0", "eslint-flat-config-utils": "^2.1.0", "eslint-plugin-antfu": "^3.1.1", "eslint-plugin-import-x": "^4.15.0", "eslint-plugin-jsonc": "^2.20.1", "eslint-plugin-perfectionist": "^4.13.0", "eslint-plugin-pnpm": "^0.3.1", "eslint-plugin-regexp": "^2.7.0", "eslint-plugin-unicorn": "^60.0.0", "eslint-plugin-vue": "^10.1.0", "eslint-typegen": "^2.2.0", "exsolve": "^1.0.5", "globals": "^16.2.0", "jsonc-eslint-parser": "^2.4.0", "kolorist": "^1.8.0", "local-pkg": "^1.1.1", "nypm": "^0.6.0", "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1", "typescript-eslint": "^8.33.0", "vue-eslint-parser": "^10.1.3", "yaml-eslint-parser": "^1.3.0" }, "peerDependencies": { "@vitest/eslint-plugin": "^1.1.42", "eslint": "^9.5.0", "eslint-plugin-jest": "^28.0.0", "eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-vuejs-accessibility": "^2.0.0" }, "bin": { "eslint-config-vuetify": "bin/cli.mjs" } }, "sha512-MS46PCbUGuuNWJ3R+uP8oGo0HDmdWZAxMqw0hRAYCWsFLTixDVlNLIM+DyQf4ay77FBZl5wambTn6I8hcTbUUw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-flat-config-utils": ["eslint-flat-config-utils@2.1.1", "https://registry.npmmirror.com/eslint-flat-config-utils/-/eslint-flat-config-utils-2.1.1.tgz", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-K8eaPkBemHkfbYsZH7z4lZ/tt6gNSsVh535Wh9W9gQBS2WjvfUbbVr2NZR3L1yiRCLuOEimYfPxCxODczD4Opg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-import-context": ["eslint-import-context@0.1.9", "https://registry.npmmirror.com/eslint-import-context/-/eslint-import-context-0.1.9.tgz", { "dependencies": { "get-tsconfig": "^4.10.1", "stable-hash-x": "^0.2.0" }, "peerDependencies": { "unrs-resolver": "^1.0.0" }, "optionalPeers": ["unrs-resolver"] }, "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-json-compat-utils": ["eslint-json-compat-utils@0.2.1", "https://registry.npmmirror.com/eslint-json-compat-utils/-/eslint-json-compat-utils-0.2.1.tgz", { "dependencies": { "esquery": "^1.6.0" }, "peerDependencies": { "eslint": "*", "jsonc-eslint-parser": "^2.4.0" } }, "sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-antfu": ["eslint-plugin-antfu@3.1.1", "https://registry.npmmirror.com/eslint-plugin-antfu/-/eslint-plugin-antfu-3.1.1.tgz", { "peerDependencies": { "eslint": "*" } }, "sha512-7Q+NhwLfHJFvopI2HBZbSxWXngTwBLKxW1AGXLr2lEGxcEIK/AsDs8pn8fvIizl5aZjBbVbVK5ujmMpBe4Tvdg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-import-x": ["eslint-plugin-import-x@4.16.1", "https://registry.npmmirror.com/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz", { "dependencies": { "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", "debug": "^4.4.1", "eslint-import-context": "^0.1.9", "is-glob": "^4.0.3", "minimatch": "^9.0.3 || ^10.0.1", "semver": "^7.7.2", "stable-hash-x": "^0.2.0", "unrs-resolver": "^1.9.2" }, "peerDependencies": { "@typescript-eslint/utils": "^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "eslint-import-resolver-node": "*" }, "optionalPeers": ["@typescript-eslint/utils", "eslint-import-resolver-node"] }, "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-jest": ["eslint-plugin-jest@28.14.0", "https://registry.npmmirror.com/eslint-plugin-jest/-/eslint-plugin-jest-28.14.0.tgz", { "dependencies": { "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependencies": { "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", "jest": "*" }, "optionalPeers": ["@typescript-eslint/eslint-plugin", "jest"] }, "sha512-P9s/qXSMTpRTerE2FQ0qJet2gKbcGyFTPAJipoKxmWqR6uuFqIqk8FuEfg5yBieOezVrEfAMZrEwJ6yEp+1MFQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-jsonc": ["eslint-plugin-jsonc@2.20.1", "https://registry.npmmirror.com/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.20.1.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.5.1", "eslint-compat-utils": "^0.6.4", "eslint-json-compat-utils": "^0.2.1", "espree": "^9.6.1 || ^10.3.0", "graphemer": "^1.4.0", "jsonc-eslint-parser": "^2.4.0", "natural-compare": "^1.4.0", "synckit": "^0.6.2 || ^0.7.3 || ^0.11.5" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-gUzIwQHXx7ZPypUoadcyRi4WbHW2TPixDr0kqQ4miuJBU0emJmyGTlnaT3Og9X2a8R1CDayN9BFSq5weGWbTng=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-no-only-tests": ["eslint-plugin-no-only-tests@3.3.0", "https://registry.npmmirror.com/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.3.0.tgz", {}, "sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-perfectionist": ["eslint-plugin-perfectionist@4.15.0", "https://registry.npmmirror.com/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-4.15.0.tgz", { "dependencies": { "@typescript-eslint/types": "^8.34.1", "@typescript-eslint/utils": "^8.34.1", "natural-orderby": "^5.0.0" }, "peerDependencies": { "eslint": ">=8.45.0" } }, "sha512-pC7PgoXyDnEXe14xvRUhBII8A3zRgggKqJFx2a82fjrItDs1BSI7zdZnQtM2yQvcyod6/ujmzb7ejKPx8lZTnw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-pnpm": ["eslint-plugin-pnpm@0.3.1", "https://registry.npmmirror.com/eslint-plugin-pnpm/-/eslint-plugin-pnpm-0.3.1.tgz", { "dependencies": { "find-up-simple": "^1.0.1", "jsonc-eslint-parser": "^2.4.0", "pathe": "^2.0.3", "pnpm-workspace-yaml": "0.3.1", "tinyglobby": "^0.2.12", "yaml-eslint-parser": "^1.3.0" }, "peerDependencies": { "eslint": "^9.0.0" } }, "sha512-vi5iHoELIAlBbX4AW8ZGzU3tUnfxuXhC/NKo3qRcI5o9igbz6zJUqSlQ03bPeMqWIGTPatZnbWsNR1RnlNERNQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-regexp": ["eslint-plugin-regexp@2.10.0", "https://registry.npmmirror.com/eslint-plugin-regexp/-/eslint-plugin-regexp-2.10.0.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "comment-parser": "^1.4.0", "jsdoc-type-pratt-parser": "^4.0.0", "refa": "^0.12.1", "regexp-ast-analysis": "^0.7.1", "scslre": "^0.3.0" }, "peerDependencies": { "eslint": ">=8.44.0" } }, "sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-unicorn": ["eslint-plugin-unicorn@60.0.0", "https://registry.npmmirror.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "@eslint-community/eslint-utils": "^4.7.0", "@eslint/plugin-kit": "^0.3.3", "change-case": "^5.4.4", "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "peerDependencies": { "eslint": ">=9.29.0" } }, "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-vue": ["eslint-plugin-vue@10.4.0", "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-10.4.0.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" }, "optionalPeers": ["@typescript-eslint/parser"] }, "sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-vuejs-accessibility": ["eslint-plugin-vuejs-accessibility@2.4.1", "https://registry.npmmirror.com/eslint-plugin-vuejs-accessibility/-/eslint-plugin-vuejs-accessibility-2.4.1.tgz", { "dependencies": { "aria-query": "^5.3.0", "emoji-regex": "^10.0.0", "vue-eslint-parser": "^9.0.1" }, "peerDependencies": { "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-ZRZhPdslplZXSF71MtSG+zXYRAT5KiHR4JVuo/DERQf9noAkDvi5W418VOE1qllmJd7wTenndxi1q8XeDMxdHw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-scope": ["eslint-scope@8.4.0", "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-typegen": ["eslint-typegen@2.3.0", "https://registry.npmmirror.com/eslint-typegen/-/eslint-typegen-2.3.0.tgz", { "dependencies": { "json-schema-to-typescript-lite": "^15.0.0", "ohash": "^2.0.11" }, "peerDependencies": { "eslint": "^9.0.0" } }, "sha512-azYgAvhlz1AyTpeLfVSKcrNJInuIsRrcUrOcHmEl8T9oMKesePVUPrF8gRgE6azV8CAlFzxJDTyaXAAbA/BYiA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "espree": ["espree@10.4.0", "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "esquery": ["esquery@1.6.0", "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "esrecurse": ["esrecurse@4.3.0", "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "estraverse": ["estraverse@5.3.0", "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "estree-walker": ["estree-walker@2.0.2", "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "esutils": ["esutils@2.0.3", "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "exsolve": ["exsolve@1.0.7", "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.7.tgz", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fast-glob": ["fast-glob@3.3.3", "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fast-levenshtein": ["fast-levenshtein@2.0.6", "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fastq": ["fastq@1.19.1", "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fdir": ["fdir@6.5.0", "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "file-entry-cache": ["file-entry-cache@8.0.0", "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fill-range": ["fill-range@7.1.1", "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "find-up": ["find-up@5.0.0", "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "find-up-simple": ["find-up-simple@1.0.1", "https://registry.npmmirror.com/find-up-simple/-/find-up-simple-1.0.1.tgz", {}, "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "flat-cache": ["flat-cache@4.0.1", "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "flatted": ["flatted@3.3.3", "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "framesync": ["framesync@6.1.2", "https://registry.npmmirror.com/framesync/-/framesync-6.1.2.tgz", { "dependencies": { "tslib": "2.4.0" } }, "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fsevents": ["fsevents@2.3.3", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "get-tsconfig": ["get-tsconfig@4.10.1", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.10.1.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "glob-parent": ["glob-parent@6.0.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "globals": ["globals@16.3.0", "https://registry.npmmirror.com/globals/-/globals-16.3.0.tgz", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "graphemer": ["graphemer@1.4.0", "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "has-flag": ["has-flag@4.0.0", "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "he": ["he@1.2.0", "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "hey-listen": ["hey-listen@1.0.8", "https://registry.npmmirror.com/hey-listen/-/hey-listen-1.0.8.tgz", {}, "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "hookable": ["hookable@5.5.3", "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ignore": ["ignore@5.3.2", "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "immutable": ["immutable@5.1.3", "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz", {}, "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "import-fresh": ["import-fresh@3.3.1", "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "imurmurhash": ["imurmurhash@0.1.4", "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "indent-string": ["indent-string@5.0.0", "https://registry.npmmirror.com/indent-string/-/indent-string-5.0.0.tgz", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "is-binary-path": ["is-binary-path@2.1.0", "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "is-builtin-module": ["is-builtin-module@5.0.0", "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-5.0.0.tgz", { "dependencies": { "builtin-modules": "^5.0.0" } }, "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "is-extglob": ["is-extglob@2.1.1", "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "is-glob": ["is-glob@4.0.3", "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "is-number": ["is-number@7.0.0", "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "is-what": ["is-what@4.1.16", "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "isexe": ["isexe@3.1.1", "https://registry.npmmirror.com/isexe/-/isexe-3.1.1.tgz", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "js-yaml": ["js-yaml@4.1.0", "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@4.8.0", "https://registry.npmmirror.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.8.0.tgz", {}, "sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "jsesc": ["jsesc@3.1.0", "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "json-buffer": ["json-buffer@3.0.1", "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "json-parse-even-better-errors": ["json-parse-even-better-errors@4.0.0", "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", {}, "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "json-schema-to-typescript-lite": ["json-schema-to-typescript-lite@15.0.0", "https://registry.npmmirror.com/json-schema-to-typescript-lite/-/json-schema-to-typescript-lite-15.0.0.tgz", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^14.1.1", "@types/json-schema": "^7.0.15" } }, "sha512-5mMORSQm9oTLyjM4mWnyNBi2T042Fhg1/0gCIB6X8U/LVpM2A+Nmj2yEyArqVouDmFThDxpEXcnTgSrjkGJRFA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "json-schema-traverse": ["json-schema-traverse@0.4.1", "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "json5": ["json5@2.2.3", "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "jsonc-eslint-parser": ["jsonc-eslint-parser@2.4.0", "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz", { "dependencies": { "acorn": "^8.5.0", "eslint-visitor-keys": "^3.0.0", "espree": "^9.0.0", "semver": "^7.3.5" } }, "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "keyv": ["keyv@4.5.4", "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "kolorist": ["kolorist@1.8.0", "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "levn": ["levn@0.4.1", "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "local-pkg": ["local-pkg@1.1.2", "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "locate-path": ["locate-path@6.0.0", "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "lodash": ["lodash@4.17.21", "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "lodash.merge": ["lodash.merge@4.6.2", "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "magic-string": ["magic-string@0.30.18", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.18.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "magic-string-ast": ["magic-string-ast@1.0.2", "https://registry.npmmirror.com/magic-string-ast/-/magic-string-ast-1.0.2.tgz", { "dependencies": { "magic-string": "^0.30.17" } }, "sha512-8ngQgLhcT0t3YBdn9CGkZqCYlvwW9pm7aWJwd7AxseVWf1RU8ZHCQvG1mt3N5vvUme+pXTcHB8G/7fE666U8Vw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "memorystream": ["memorystream@0.3.1", "https://registry.npmmirror.com/memorystream/-/memorystream-0.3.1.tgz", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "merge2": ["merge2@1.4.1", "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "micromatch": ["micromatch@4.0.8", "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "min-indent": ["min-indent@1.0.1", "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "minimatch": ["minimatch@3.1.2", "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "mitt": ["mitt@3.0.1", "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "mlly": ["mlly@1.8.0", "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ms": ["ms@2.1.3", "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "muggle-string": ["muggle-string@0.4.1", "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "napi-postinstall": ["napi-postinstall@0.3.3", "https://registry.npmmirror.com/napi-postinstall/-/napi-postinstall-0.3.3.tgz", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "natural-compare": ["natural-compare@1.4.0", "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "natural-orderby": ["natural-orderby@5.0.0", "https://registry.npmmirror.com/natural-orderby/-/natural-orderby-5.0.0.tgz", {}, "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "node-addon-api": ["node-addon-api@7.1.1", "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "node-releases": ["node-releases@2.0.19", "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "normalize-path": ["normalize-path@3.0.0", "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "npm-run-all2": ["npm-run-all2@7.0.2", "https://registry.npmmirror.com/npm-run-all2/-/npm-run-all2-7.0.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "minimatch": "^9.0.0", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-7tXR+r9hzRNOPNTvXegM+QzCuMjzUIIq66VDunL6j60O4RrExx32XUhlrS7UK4VcdGw5/Wxzb3kfNcFix9JKDA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "nth-check": ["nth-check@2.1.1", "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "nypm": ["nypm@0.6.1", "https://registry.npmmirror.com/nypm/-/nypm-0.6.1.tgz", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.2.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ohash": ["ohash@2.0.11", "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "optionator": ["optionator@0.9.4", "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "p-limit": ["p-limit@3.1.0", "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "p-locate": ["p-locate@5.0.0", "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "package-manager-detector": ["package-manager-detector@1.3.0", "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.3.0.tgz", {}, "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "parent-module": ["parent-module@1.0.1", "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "path-browserify": ["path-browserify@1.0.1", "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "path-exists": ["path-exists@4.0.0", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "path-key": ["path-key@3.1.1", "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "pathe": ["pathe@2.0.3", "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "perfect-debounce": ["perfect-debounce@1.0.0", "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "picocolors": ["picocolors@1.1.1", "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "picomatch": ["picomatch@4.0.3", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "pidtree": ["pidtree@0.6.0", "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "pinia": ["pinia@3.0.3", "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz", { "dependencies": { "@vue/devtools-api": "^7.7.2" }, "peerDependencies": { "typescript": ">=4.4.4", "vue": "^2.7.0 || ^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "pkg-types": ["pkg-types@2.3.0", "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "pluralize": ["pluralize@8.0.0", "https://registry.npmmirror.com/pluralize/-/pluralize-8.0.0.tgz", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "pnpm-workspace-yaml": ["pnpm-workspace-yaml@0.3.1", "https://registry.npmmirror.com/pnpm-workspace-yaml/-/pnpm-workspace-yaml-0.3.1.tgz", { "dependencies": { "yaml": "^2.7.0" } }, "sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "popmotion": ["popmotion@11.0.5", "https://registry.npmmirror.com/popmotion/-/popmotion-11.0.5.tgz", { "dependencies": { "framesync": "6.1.2", "hey-listen": "^1.0.8", "style-value-types": "5.1.2", "tslib": "2.4.0" } }, "sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "postcss": ["postcss@8.5.6", "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "prelude-ls": ["prelude-ls@1.2.1", "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "punycode": ["punycode@2.3.1", "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "quansync": ["quansync@0.2.11", "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "queue-microtask": ["queue-microtask@1.2.3", "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "read-package-json-fast": ["read-package-json-fast@4.0.0", "https://registry.npmmirror.com/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "readdirp": ["readdirp@3.6.0", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "refa": ["refa@0.12.1", "https://registry.npmmirror.com/refa/-/refa-0.12.1.tgz", { "dependencies": { "@eslint-community/regexpp": "^4.8.0" } }, "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "regexp-ast-analysis": ["regexp-ast-analysis@0.7.1", "https://registry.npmmirror.com/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.1" } }, "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "regexp-tree": ["regexp-tree@0.1.27", "https://registry.npmmirror.com/regexp-tree/-/regexp-tree-0.1.27.tgz", { "bin": { "regexp-tree": "bin/regexp-tree" } }, "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "regjsparser": ["regjsparser@0.12.0", "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.12.0.tgz", { "dependencies": { "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "resolve-from": ["resolve-from@4.0.0", "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "reusify": ["reusify@1.1.0", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "rfdc": ["rfdc@1.4.1", "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "rollup": ["rollup@4.50.0", "https://registry.npmmirror.com/rollup/-/rollup-4.50.0.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.0", "@rollup/rollup-android-arm64": "4.50.0", "@rollup/rollup-darwin-arm64": "4.50.0", "@rollup/rollup-darwin-x64": "4.50.0", "@rollup/rollup-freebsd-arm64": "4.50.0", "@rollup/rollup-freebsd-x64": "4.50.0", "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", "@rollup/rollup-linux-arm-musleabihf": "4.50.0", "@rollup/rollup-linux-arm64-gnu": "4.50.0", "@rollup/rollup-linux-arm64-musl": "4.50.0", "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", "@rollup/rollup-linux-ppc64-gnu": "4.50.0", "@rollup/rollup-linux-riscv64-gnu": "4.50.0", "@rollup/rollup-linux-riscv64-musl": "4.50.0", "@rollup/rollup-linux-s390x-gnu": "4.50.0", "@rollup/rollup-linux-x64-gnu": "4.50.0", "@rollup/rollup-linux-x64-musl": "4.50.0", "@rollup/rollup-openharmony-arm64": "4.50.0", "@rollup/rollup-win32-arm64-msvc": "4.50.0", "@rollup/rollup-win32-ia32-msvc": "4.50.0", "@rollup/rollup-win32-x64-msvc": "4.50.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "run-parallel": ["run-parallel@1.2.0", "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "rxjs": ["rxjs@7.8.2", "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.2.tgz", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass": ["sass@1.91.0", "https://registry.npmmirror.com/sass/-/sass-1.91.0.tgz", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded": ["sass-embedded@1.91.0", "https://registry.npmmirror.com/sass-embedded/-/sass-embedded-1.91.0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.5.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-all-unknown": "1.91.0", "sass-embedded-android-arm": "1.91.0", "sass-embedded-android-arm64": "1.91.0", "sass-embedded-android-riscv64": "1.91.0", "sass-embedded-android-x64": "1.91.0", "sass-embedded-darwin-arm64": "1.91.0", "sass-embedded-darwin-x64": "1.91.0", "sass-embedded-linux-arm": "1.91.0", "sass-embedded-linux-arm64": "1.91.0", "sass-embedded-linux-musl-arm": "1.91.0", "sass-embedded-linux-musl-arm64": "1.91.0", "sass-embedded-linux-musl-riscv64": "1.91.0", "sass-embedded-linux-musl-x64": "1.91.0", "sass-embedded-linux-riscv64": "1.91.0", "sass-embedded-linux-x64": "1.91.0", "sass-embedded-unknown-all": "1.91.0", "sass-embedded-win32-arm64": "1.91.0", "sass-embedded-win32-x64": "1.91.0" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-VTckYcH1AglrZ3VpPETilTo3Ef472XKwP13lrNfbOHSR6Eo5p27XTkIi+6lrCbuhBFFGAmy+4BRoLaeFUgn+eg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-all-unknown": ["sass-embedded-all-unknown@1.91.0", "https://registry.npmmirror.com/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.91.0.tgz", { "dependencies": { "sass": "1.91.0" }, "cpu": [ "!arm", "!x64", "!arm64", ] }, "sha512-AXC1oPqDfLnLtcoxM+XwSnbhcQs0TxAiA5JDEstl6+tt6fhFLKxdyl1Hla39SFtxvMfB2QDUYE3Dmx49O59vYg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-android-arm": ["sass-embedded-android-arm@1.91.0", "https://registry.npmmirror.com/sass-embedded-android-arm/-/sass-embedded-android-arm-1.91.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-DSh1V8TlLIcpklAbn4NINEFs3yD2OzVTbawEXK93IH990upoGNFVNRTstFQ/gcvlbWph3Y3FjAJvo37zUO485A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-android-arm64": ["sass-embedded-android-arm64@1.91.0", "https://registry.npmmirror.com/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.91.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-I8Eeg2CeVcZIhXcQLNEY6ZBRF0m7jc818/fypwMwvIdbxGWBekTzc3aKHTLhdBpFzGnDIyR4s7oB0/OjIpzD1A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-android-riscv64": ["sass-embedded-android-riscv64@1.91.0", "https://registry.npmmirror.com/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.91.0.tgz", { "os": "android", "cpu": "none" }, "sha512-qmsl1a7IIJL0fCOwzmRB+6nxeJK5m9/W8LReXUrdgyJNH5RyxChDg+wwQPVATFffOuztmWMnlJ5CV2sCLZrXcQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-android-x64": ["sass-embedded-android-x64@1.91.0", "https://registry.npmmirror.com/sass-embedded-android-x64/-/sass-embedded-android-x64-1.91.0.tgz", { "os": "android", "cpu": "x64" }, "sha512-/wN0HBLATOVSeN3Tzg0yxxNTo1IQvOxxxwFv7Ki/1/UCg2AqZPxTpNoZj/mn8tUPtiVogMGbC8qclYMq1aRZsQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-darwin-arm64": ["sass-embedded-darwin-arm64@1.91.0", "https://registry.npmmirror.com/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.91.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-gQ6ScInxAN+BDUXy426BSYLRawkmGYlHpQ9i6iOxorr64dtIb3l6eb9YaBV8lPlroUnugylmwN2B3FU9BuPfhA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-darwin-x64": ["sass-embedded-darwin-x64@1.91.0", "https://registry.npmmirror.com/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.91.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-DSvFMtECL2blYVTFMO5fLeNr5bX437Lrz8R47fdo5438TRyOkSgwKTkECkfh3YbnrL86yJIN2QQlmBMF17Z/iw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-arm": ["sass-embedded-linux-arm@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.91.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-ppAZLp3eZ9oTjYdQDf4nM7EehDpkxq5H1hE8FOrx8LpY7pxn6QF+SRpAbRjdfFChRw0K7vh+IiCnQEMp7uLNAg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-arm64": ["sass-embedded-linux-arm64@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.91.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-OnKCabD7f420ZEC/6YI9WhCVGMZF+ybZ5NbAB9SsG1xlxrKbWQ1s7CIl0w/6RDALtJ+Fjn8+mrxsxqakoAkeuA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-musl-arm": ["sass-embedded-linux-musl-arm@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.91.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-znEsNC2FurPF9+XwQQ6e/fVoic3e5D3/kMB41t/bE8byJVRdaPhkdsszt3pZUE56nNGYoCuieSXUkk7VvyPHsw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-musl-arm64": ["sass-embedded-linux-musl-arm64@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.91.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-VfbPpID1C5TT7rukob6CKgefx/TsLE+XZieMNd00hvfJ8XhqPr5DGvSMCNpXlwaedzTirbJu357m+n2PJI9TFQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-musl-riscv64": ["sass-embedded-linux-musl-riscv64@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.91.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-ZfLGldKEEeZjuljKks835LTq7jDRI3gXsKKXXgZGzN6Yymd4UpBOGWiDQlWsWTvw5UwDU2xfFh0wSXbLGHTjVA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-musl-x64": ["sass-embedded-linux-musl-x64@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.91.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-4kSiSGPKFMbLvTRbP/ibyiKheOA3fwsJKWU0SOuekSPmybMdrhNkTm0REp6+nehZRE60kC3lXmEV4a7w8Jrwyg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-riscv64": ["sass-embedded-linux-riscv64@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.91.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-Y3Fj94SYYvMX9yo49T78yBgBWXtG3EyYUT5K05XyCYkcdl1mVXJSrEmqmRfe4vQGUCaSe/6s7MmsA9Q+mQez7Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-linux-x64": ["sass-embedded-linux-x64@1.91.0", "https://registry.npmmirror.com/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.91.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-XwIUaE7pQP/ezS5te80hlyheYiUlo0FolQ0HBtxohpavM+DVX2fjwFm5LOUJHrLAqP+TLBtChfFeLj1Ie4Aenw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-unknown-all": ["sass-embedded-unknown-all@1.91.0", "https://registry.npmmirror.com/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.91.0.tgz", { "dependencies": { "sass": "1.91.0" }, "os": [ "!linux", "!win32", "!darwin", "!android", ] }, "sha512-Bj6v7ScQp/HtO91QBy6ood9AArSIN7/RNcT4E7P9QoY3o+e6621Vd28lV81vdepPrt6u6PgJoVKmLNODqB6Q+A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-win32-arm64": ["sass-embedded-win32-arm64@1.91.0", "https://registry.npmmirror.com/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.91.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-yDCwTiPRex03i1yo7LwiAl1YQ21UyfOxPobD7UjI8AE8ZcB0mQ28VVX66lsZ+qm91jfLslNFOFCD4v79xCG9hA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.91.0", "https://registry.npmmirror.com/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.91.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-wiuMz/cx4vsk6rYCnNyoGE5pd73aDJ/zF3qJDose3ZLT1/vV943doJE5pICnS/v5DrUqzV6a1CNq4fN+xeSgFQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "scslre": ["scslre@0.3.0", "https://registry.npmmirror.com/scslre/-/scslre-0.3.0.tgz", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.0", "regexp-ast-analysis": "^0.7.0" } }, "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "scule": ["scule@1.3.0", "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "semver": ["semver@7.7.2", "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "shebang-command": ["shebang-command@2.0.0", "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "shebang-regex": ["shebang-regex@3.0.0", "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "shell-quote": ["shell-quote@1.8.3", "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sisteransi": ["sisteransi@1.0.5", "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "source-map-js": ["source-map-js@1.2.1", "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "speakingurl": ["speakingurl@14.0.1", "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "stable-hash-x": ["stable-hash-x@0.2.0", "https://registry.npmmirror.com/stable-hash-x/-/stable-hash-x-0.2.0.tgz", {}, "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "strip-indent": ["strip-indent@4.0.0", "https://registry.npmmirror.com/strip-indent/-/strip-indent-4.0.0.tgz", { "dependencies": { "min-indent": "^1.0.1" } }, "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "strip-json-comments": ["strip-json-comments@3.1.1", "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "style-value-types": ["style-value-types@5.1.2", "https://registry.npmmirror.com/style-value-types/-/style-value-types-5.1.2.tgz", { "dependencies": { "hey-listen": "^1.0.8", "tslib": "2.4.0" } }, "sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "superjson": ["superjson@2.2.2", "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz", { "dependencies": { "copy-anything": "^3.0.2" } }, "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "supports-color": ["supports-color@8.1.1", "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sync-child-process": ["sync-child-process@1.0.2", "https://registry.npmmirror.com/sync-child-process/-/sync-child-process-1.0.2.tgz", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sync-message-port": ["sync-message-port@1.1.3", "https://registry.npmmirror.com/sync-message-port/-/sync-message-port-1.1.3.tgz", {}, "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "synckit": ["synckit@0.11.11", "https://registry.npmmirror.com/synckit/-/synckit-0.11.11.tgz", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "tinyexec": ["tinyexec@1.0.1", "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.1.tgz", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "tinyglobby": ["tinyglobby@0.2.14", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "to-regex-range": ["to-regex-range@5.0.1", "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ts-api-utils": ["ts-api-utils@2.1.0", "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "tslib": ["tslib@2.4.0", "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz", {}, "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "type-check": ["type-check@0.4.0", "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "typescript": ["typescript@5.8.3", "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "typescript-eslint": ["typescript-eslint@8.41.0", "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.41.0.tgz", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.41.0", "@typescript-eslint/parser": "8.41.0", "@typescript-eslint/typescript-estree": "8.41.0", "@typescript-eslint/utils": "8.41.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "ufo": ["ufo@1.6.1", "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "undici-types": ["undici-types@6.21.0", "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unplugin": ["unplugin@2.3.5", "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.5.tgz", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unplugin-fonts": ["unplugin-fonts@1.4.0", "https://registry.npmmirror.com/unplugin-fonts/-/unplugin-fonts-1.4.0.tgz", { "dependencies": { "fast-glob": "^3.3.3", "unplugin": "2.3.5" }, "peerDependencies": { "@nuxt/kit": "^3.0.0 || ^4.0.0", "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@nuxt/kit"] }, "sha512-TIJqr5rSlK/+3oL5nnrrEJ+Ty2taQ/bTJY1C5abYnksl553Q3HoHVqS4pnRLDkwpZq8AYqywib3kEVvHH+CtRQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unplugin-utils": ["unplugin-utils@0.2.5", "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.2.5.tgz", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unplugin-vue-components": ["unplugin-vue-components@28.8.0", "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-28.8.0.tgz", { "dependencies": { "chokidar": "^3.6.0", "debug": "^4.4.1", "local-pkg": "^1.1.1", "magic-string": "^0.30.17", "mlly": "^1.7.4", "tinyglobby": "^0.2.14", "unplugin": "^2.3.5", "unplugin-utils": "^0.2.4" }, "peerDependencies": { "@babel/parser": "^7.15.8", "@nuxt/kit": "^3.2.2 || ^4.0.0", "vue": "2 || 3" }, "optionalPeers": ["@babel/parser", "@nuxt/kit"] }, "sha512-2Q6ZongpoQzuXDK0ZsVzMoshH0MWZQ1pzVL538G7oIDKRTVzHjppBDS8aB99SADGHN3lpGU7frraCG6yWNoL5Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unplugin-vue-router": ["unplugin-vue-router@0.14.0", "https://registry.npmmirror.com/unplugin-vue-router/-/unplugin-vue-router-0.14.0.tgz", { "dependencies": { "@vue-macros/common": "3.0.0-beta.15", "ast-walker-scope": "^0.8.1", "chokidar": "^4.0.3", "fast-glob": "^3.3.3", "json5": "^2.2.3", "local-pkg": "^1.1.1", "magic-string": "^0.30.17", "mlly": "^1.7.4", "pathe": "^2.0.3", "picomatch": "^4.0.2", "scule": "^1.3.0", "unplugin": "^2.3.5", "unplugin-utils": "^0.2.4", "yaml": "^2.8.0" }, "peerDependencies": { "@vue/compiler-sfc": "^3.5.17", "vue-router": "^4.5.1" }, "optionalPeers": ["vue-router"] }, "sha512-ipjunvS5e2aFHBAUFuLbHl2aHKbXXXBhTxGT9wZx66fNVPdEQzVVitF8nODr1plANhTTa3UZ+DQu9uyLngMzoQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unrs-resolver": ["unrs-resolver@1.11.1", "https://registry.npmmirror.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "upath": ["upath@2.0.1", "https://registry.npmmirror.com/upath/-/upath-2.0.1.tgz", {}, "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "update-browserslist-db": ["update-browserslist-db@1.1.3", "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "uri-js": ["uri-js@4.4.1", "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "util-deprecate": ["util-deprecate@1.0.2", "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "varint": ["varint@6.0.0", "https://registry.npmmirror.com/varint/-/varint-6.0.0.tgz", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vite": ["vite@6.3.5", "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vite-plugin-vuetify": ["vite-plugin-vuetify@2.1.2", "https://registry.npmmirror.com/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.2.tgz", { "dependencies": { "@vuetify/loader-shared": "^2.1.1", "debug": "^4.3.3", "upath": "^2.0.1" }, "peerDependencies": { "vite": ">=5", "vue": "^3.0.0", "vuetify": "^3.0.0" } }, "sha512-I/wd6QS+DO6lHmuGoi1UTyvvBTQ2KDzQZ9oowJQEJ6OcjWfJnscYXx2ptm6S7fJSASuZT8jGRBL3LV4oS3LpaA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vscode-uri": ["vscode-uri@3.1.0", "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vue": ["vue@3.5.20", "https://registry.npmmirror.com/vue/-/vue-3.5.20.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.20", "@vue/compiler-sfc": "3.5.20", "@vue/runtime-dom": "3.5.20", "@vue/server-renderer": "3.5.20", "@vue/shared": "3.5.20" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-2sBz0x/wis5TkF1XZ2vH25zWq3G1bFEPOfkBcx2ikowmphoQsPH6X0V3mmPCXA2K1N/XGTnifVyDQP4GfDDeQw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vue-eslint-parser": ["vue-eslint-parser@10.2.0", "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", { "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.6.0", "semver": "^7.6.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vue-router": ["vue-router@4.5.1", "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.2.0" } }, "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vue-tsc": ["vue-tsc@3.0.6", "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-3.0.6.tgz", { "dependencies": { "@volar/typescript": "2.4.23", "@vue/language-core": "3.0.6" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-Tbs8Whd43R2e2nxez4WXPvvdjGbW24rOSgRhLOHXzWiT4pcP4G7KeWh0YCn18rF4bVwv7tggLLZ6MJnO6jXPBg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "vuetify": ["vuetify@3.9.6", "https://registry.npmmirror.com/vuetify/-/vuetify-3.9.6.tgz", { "peerDependencies": { "typescript": ">=4.7", "vite-plugin-vuetify": ">=2.1.0", "vue": "^3.5.0", "webpack-plugin-vuetify": ">=3.1.0" }, "optionalPeers": ["typescript", "vite-plugin-vuetify", "webpack-plugin-vuetify"] }, "sha512-jNs2yLYiM50kE16gBu58xmnh9t/MOvgnYcNvmLNps6TLq9rPvjTNFm2k2jWfe69hGg0gQf+MFXXDkf65fxi9gg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "which": ["which@5.0.0", "https://registry.npmmirror.com/which/-/which-5.0.0.tgz", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "word-wrap": ["word-wrap@1.2.5", "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "xml-name-validator": ["xml-name-validator@4.0.0", "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "yaml": ["yaml@2.8.1", "https://registry.npmmirror.com/yaml/-/yaml-2.8.1.tgz", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "yaml-eslint-parser": ["yaml-eslint-parser@1.3.0", "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-1.3.0.tgz", { "dependencies": { "eslint-visitor-keys": "^3.0.0", "yaml": "^2.0.0" } }, "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "yocto-queue": ["yocto-queue@0.1.0", "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@emnapi/core/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@emnapi/runtime/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@eslint/eslintrc/globals": ["globals@14.0.0", "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.3.1.tgz", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@tybys/wasm-util/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "anymatch/picomatch": ["picomatch@2.3.1", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "chalk/ansi-styles": ["ansi-styles@4.3.0", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "chalk/supports-color": ["supports-color@7.2.0", "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "chokidar/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "clean-regexp/escape-string-regexp": ["escape-string-regexp@1.0.5", "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "cross-spawn/which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-import-x/minimatch": ["minimatch@9.0.5", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-vuejs-accessibility/vue-eslint-parser": ["vue-eslint-parser@9.4.3", "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", { "dependencies": { "debug": "^4.3.4", "eslint-scope": "^7.1.1", "eslint-visitor-keys": "^3.3.0", "espree": "^9.3.1", "esquery": "^1.4.0", "lodash": "^4.17.21", "semver": "^7.3.6" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "fast-glob/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "jsonc-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "jsonc-eslint-parser/espree": ["espree@9.6.1", "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "micromatch/picomatch": ["picomatch@2.3.1", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "mlly/pkg-types": ["pkg-types@1.3.1", "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "npm-run-all2/minimatch": ["minimatch@9.0.5", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "pinia/@vue/devtools-api": ["@vue/devtools-api@7.7.7", "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz", { "dependencies": { "@vue/devtools-kit": "^7.7.7" } }, "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "readdirp/picomatch": ["picomatch@2.3.1", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "regjsparser/jsesc": ["jsesc@3.0.2", "https://registry.npmmirror.com/jsesc/-/jsesc-3.0.2.tgz", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "rxjs/tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass/chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unplugin-vue-router/chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "yaml-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "cross-spawn/which/isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-import-x/minimatch/brace-expansion": ["brace-expansion@2.0.2", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-vuejs-accessibility/vue-eslint-parser/eslint-scope": ["eslint-scope@7.2.2", "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-vuejs-accessibility/vue-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "eslint-plugin-vuejs-accessibility/vue-eslint-parser/espree": ["espree@9.6.1", "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "mlly/pkg-types/confbox": ["confbox@0.1.8", "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "npm-run-all2/minimatch/brace-expansion": ["brace-expansion@2.0.2", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "sass/chokidar/readdirp": ["readdirp@4.1.2", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    "unplugin-vue-router/chokidar/readdirp": ["readdirp@4.1.2", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					/// <reference types="vite/client" />
 | 
				
			||||||
 | 
					/// <reference types="unplugin-vue-router/client" />
 | 
				
			||||||
							
								
								
									
										2
									
								
								eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					// 完全禁用所有 ESLint 检查
 | 
				
			||||||
 | 
					export default [];
 | 
				
			||||||
							
								
								
									
										13
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
 | 
					    <link rel="icon" href="/favicon.ico">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
 | 
					    <title>Welcome to Vuetify 3</title>
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <div id="app"></div>
 | 
				
			||||||
 | 
					    <script type="module" src="/src/main.ts"></script>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										4257
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										44
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "client",
 | 
				
			||||||
 | 
					  "private": true,
 | 
				
			||||||
 | 
					  "type": "module",
 | 
				
			||||||
 | 
					  "version": "0.0.0",
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "dev": "vite",
 | 
				
			||||||
 | 
					    "build": "run-p type-check \"build-only {@}\" --",
 | 
				
			||||||
 | 
					    "preview": "vite preview",
 | 
				
			||||||
 | 
					    "build-only": "vite build",
 | 
				
			||||||
 | 
					    "type-check": "vue-tsc --build --force",
 | 
				
			||||||
 | 
					    "lint": "eslint . --fix"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@babylonjs/core": "^8.25.0",
 | 
				
			||||||
 | 
					    "@babylonjs/loaders": "^8.25.0",
 | 
				
			||||||
 | 
					    "@fontsource/roboto": "5.2.6",
 | 
				
			||||||
 | 
					    "@mdi/font": "7.4.47",
 | 
				
			||||||
 | 
					    "@types/babylonjs": "^2.4.5",
 | 
				
			||||||
 | 
					    "pinia": "^3.0.3",
 | 
				
			||||||
 | 
					    "popmotion": "^11.0.5",
 | 
				
			||||||
 | 
					    "uuid": "^13.0.0",
 | 
				
			||||||
 | 
					    "vue": "^3.5.17",
 | 
				
			||||||
 | 
					    "vuetify": "^3.9.1"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@tsconfig/node22": "^22.0.0",
 | 
				
			||||||
 | 
					    "@types/node": "^22.9.0",
 | 
				
			||||||
 | 
					    "@vitejs/plugin-vue": "^5.2.3",
 | 
				
			||||||
 | 
					    "@vue/tsconfig": "^0.7.0",
 | 
				
			||||||
 | 
					    "eslint": "^9.30.1",
 | 
				
			||||||
 | 
					    "eslint-config-vuetify": "^4.0.0",
 | 
				
			||||||
 | 
					    "npm-run-all2": "^7.0.2",
 | 
				
			||||||
 | 
					    "sass-embedded": "^1.89.2",
 | 
				
			||||||
 | 
					    "typescript": "~5.8.3",
 | 
				
			||||||
 | 
					    "unplugin-fonts": "^1.3.1",
 | 
				
			||||||
 | 
					    "unplugin-vue-components": "^28.8.0",
 | 
				
			||||||
 | 
					    "unplugin-vue-router": "^0.14.0",
 | 
				
			||||||
 | 
					    "vite": "^6.3.5",
 | 
				
			||||||
 | 
					    "vite-plugin-vuetify": "^2.1.1",
 | 
				
			||||||
 | 
					    "vue-router": "^4.5.1",
 | 
				
			||||||
 | 
					    "vue-tsc": "^3.0.1"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								public/bblcdn/babylon.ktx2Decoder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/bblcdn/draco_decoder_gltf .wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/bblcdn/draco_decoder_gltf.wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										116
									
								
								public/bblcdn/draco_wasm_wrapper_gltf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n<h.length?{done:!1,value:h[n++]}:{done:!0}}};$jscomp.arrayIterator=function(h){return{next:$jscomp.arrayIteratorImpl(h)}};$jscomp.makeIterator=function(h){var n="undefined"!=typeof Symbol&&Symbol.iterator&&h[Symbol.iterator];return n?n.call(h):$jscomp.arrayIterator(h)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
 | 
				
			||||||
 | 
					$jscomp.ISOLATE_POLYFILLS=!1;$jscomp.FORCE_POLYFILL_PROMISE=!1;$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;$jscomp.getGlobal=function(h){h=["object"==typeof globalThis&&globalThis,h,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var n=0;n<h.length;++n){var k=h[n];if(k&&k.Math==Math)return k}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
 | 
				
			||||||
 | 
					$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(h,n,k){if(h==Array.prototype||h==Object.prototype)return h;h[n]=k.value;return h};$jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";
 | 
				
			||||||
 | 
					var $jscomp$lookupPolyfilledValue=function(h,n){var k=$jscomp.propertyToPolyfillSymbol[n];if(null==k)return h[n];k=h[k];return void 0!==k?k:h[n]};$jscomp.polyfill=function(h,n,k,p){n&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(h,n,k,p):$jscomp.polyfillUnisolated(h,n,k,p))};
 | 
				
			||||||
 | 
					$jscomp.polyfillUnisolated=function(h,n,k,p){k=$jscomp.global;h=h.split(".");for(p=0;p<h.length-1;p++){var l=h[p];if(!(l in k))return;k=k[l]}h=h[h.length-1];p=k[h];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(k,h,{configurable:!0,writable:!0,value:n})};
 | 
				
			||||||
 | 
					$jscomp.polyfillIsolated=function(h,n,k,p){var l=h.split(".");h=1===l.length;p=l[0];p=!h&&p in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var y=0;y<l.length-1;y++){var f=l[y];if(!(f in p))return;p=p[f]}l=l[l.length-1];k=$jscomp.IS_SYMBOL_NATIVE&&"es6"===k?p[l]:null;n=n(k);null!=n&&(h?$jscomp.defineProperty($jscomp.polyfills,l,{configurable:!0,writable:!0,value:n}):n!==k&&(void 0===$jscomp.propertyToPolyfillSymbol[l]&&(k=1E9*Math.random()>>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE?
 | 
				
			||||||
 | 
					$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))};
 | 
				
			||||||
 | 
					$jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)};
 | 
				
			||||||
 | 
					var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q<f.length;++q){var u=f[q];f[q]=null;try{u()}catch(A){this.asyncThrow_(A)}}}this.batch_=null};n.prototype.asyncThrow_=function(f){this.asyncExecuteFunction(function(){throw f;})};var l=function(f){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];this.isRejectionHandled_=!1;var q=this.createResolveAndReject_();
 | 
				
			||||||
 | 
					try{f(q.resolve,q.reject)}catch(u){q.reject(u)}};l.prototype.createResolveAndReject_=function(){function f(A){return function(F){u||(u=!0,A.call(q,F))}}var q=this,u=!1;return{resolve:f(this.resolveTo_),reject:f(this.reject_)}};l.prototype.resolveTo_=function(f){if(f===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(f instanceof l)this.settleSameAsPromise_(f);else{a:switch(typeof f){case "object":var q=null!=f;break a;case "function":q=!0;break a;default:q=!1}q?this.resolveToNonPromiseObj_(f):
 | 
				
			||||||
 | 
					this.fulfill_(f)}};l.prototype.resolveToNonPromiseObj_=function(f){var q=void 0;try{q=f.then}catch(u){this.reject_(u);return}"function"==typeof q?this.settleSameAsThenable_(q,f):this.fulfill_(f)};l.prototype.reject_=function(f){this.settle_(2,f)};l.prototype.fulfill_=function(f){this.settle_(1,f)};l.prototype.settle_=function(f,q){if(0!=this.state_)throw Error("Cannot settle("+f+", "+q+"): Promise already settled in state"+this.state_);this.state_=f;this.result_=q;2===this.state_&&this.scheduleUnhandledRejectionCheck_();
 | 
				
			||||||
 | 
					this.executeOnSettledCallbacks_()};l.prototype.scheduleUnhandledRejectionCheck_=function(){var f=this;p(function(){if(f.notifyUnhandledRejection_()){var q=$jscomp.global.console;"undefined"!==typeof q&&q.error(f.result_)}},1)};l.prototype.notifyUnhandledRejection_=function(){if(this.isRejectionHandled_)return!1;var f=$jscomp.global.CustomEvent,q=$jscomp.global.Event,u=$jscomp.global.dispatchEvent;if("undefined"===typeof u)return!0;"function"===typeof f?f=new f("unhandledrejection",{cancelable:!0}):
 | 
				
			||||||
 | 
					"function"===typeof q?f=new q("unhandledrejection",{cancelable:!0}):(f=$jscomp.global.document.createEvent("CustomEvent"),f.initCustomEvent("unhandledrejection",!1,!0,f));f.promise=this;f.reason=this.result_;return u(f)};l.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var f=0;f<this.onSettledCallbacks_.length;++f)y.asyncExecute(this.onSettledCallbacks_[f]);this.onSettledCallbacks_=null}};var y=new n;l.prototype.settleSameAsPromise_=function(f){var q=this.createResolveAndReject_();
 | 
				
			||||||
 | 
					f.callWhenSettled_(q.resolve,q.reject)};l.prototype.settleSameAsThenable_=function(f,q){var u=this.createResolveAndReject_();try{f.call(q,u.resolve,u.reject)}catch(A){u.reject(A)}};l.prototype.then=function(f,q){function u(w,B){return"function"==typeof w?function(R){try{A(w(R))}catch(Z){F(Z)}}:B}var A,F,v=new l(function(w,B){A=w;F=B});this.callWhenSettled_(u(f,A),u(q,F));return v};l.prototype.catch=function(f){return this.then(void 0,f)};l.prototype.callWhenSettled_=function(f,q){function u(){switch(A.state_){case 1:f(A.result_);
 | 
				
			||||||
 | 
					break;case 2:q(A.result_);break;default:throw Error("Unexpected state: "+A.state_);}}var A=this;null==this.onSettledCallbacks_?y.asyncExecute(u):this.onSettledCallbacks_.push(u);this.isRejectionHandled_=!0};l.resolve=k;l.reject=function(f){return new l(function(q,u){u(f)})};l.race=function(f){return new l(function(q,u){for(var A=$jscomp.makeIterator(f),F=A.next();!F.done;F=A.next())k(F.value).callWhenSettled_(q,u)})};l.all=function(f){var q=$jscomp.makeIterator(f),u=q.next();return u.done?k([]):new l(function(A,
 | 
				
			||||||
 | 
					F){function v(R){return function(Z){w[R]=Z;B--;0==B&&A(w)}}var w=[],B=0;do w.push(void 0),B++,k(u.value).callWhenSettled_(v(w.length-1),F),u=q.next();while(!u.done)})};return l},"es6","es3");$jscomp.owns=function(h,n){return Object.prototype.hasOwnProperty.call(h,n)};$jscomp.assign=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.assign?Object.assign:function(h,n){for(var k=1;k<arguments.length;k++){var p=arguments[k];if(p)for(var l in p)$jscomp.owns(p,l)&&(h[l]=p[l])}return h};
 | 
				
			||||||
 | 
					$jscomp.polyfill("Object.assign",function(h){return h||$jscomp.assign},"es6","es3");$jscomp.checkStringArgs=function(h,n,k){if(null==h)throw new TypeError("The 'this' value for String.prototype."+k+" must not be null or undefined");if(n instanceof RegExp)throw new TypeError("First argument to String.prototype."+k+" must not be a regular expression");return h+""};
 | 
				
			||||||
 | 
					$jscomp.polyfill("String.prototype.startsWith",function(h){return h?h:function(n,k){var p=$jscomp.checkStringArgs(this,n,"startsWith");n+="";var l=p.length,y=n.length;k=Math.max(0,Math.min(k|0,p.length));for(var f=0;f<y&&k<l;)if(p[k++]!=n[f++])return!1;return f>=y}},"es6","es3");
 | 
				
			||||||
 | 
					$jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(k<p)for(;p<l;)p in this?this[k++]=this[p++]:(delete this[k++],p++);else for(l=Math.min(l,y+p-k),k+=l-p;l>p;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3");
 | 
				
			||||||
 | 
					$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
 | 
				
			||||||
 | 
					$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
 | 
				
			||||||
 | 
					var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16<b-e&&c.buffer&&ra)c=ra.decode(c.subarray(e,b));else{for(d="";e<b;){var g=c[e++];if(g&128){var t=c[e++]&63;if(192==(g&224))d+=String.fromCharCode((g&31)<<6|t);else{var aa=c[e++]&
 | 
				
			||||||
 | 
					63;g=224==(g&240)?(g&15)<<12|t<<6|aa:(g&7)<<18|t<<12|aa<<6|c[e++]&63;65536>g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e);
 | 
				
			||||||
 | 
					e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)});
 | 
				
			||||||
 | 
					if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0<e.length;)e.shift()(a)}function A(e){this.excPtr=e;this.ptr=e-24;this.set_type=function(b){Y[this.ptr+4>>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+
 | 
				
			||||||
 | 
					12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+
 | 
				
			||||||
 | 
					16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0<ba)){if(a.preRun)for("function"==
 | 
				
			||||||
 | 
					typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)ya.unshift(a.preRun.shift());u(ya);0<ba||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);e()},1)):e())}}function v(){}function w(e){return(e||v).__cache__}function B(e,b){var c=w(b),d=c[e];if(d)return d;d=Object.create((b||v).prototype);d.ptr=e;return c[e]=d}function R(e){if("string"===typeof e){for(var b=0,c=0;c<e.length;++c){var d=e.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=
 | 
				
			||||||
 | 
					d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0<d){d=c+d-1;for(var g=0;g<e.length;++g){var t=e.charCodeAt(g);if(55296<=t&&57343>=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"===
 | 
				
			||||||
 | 
					typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]=
 | 
				
			||||||
 | 
					this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr=
 | 
				
			||||||
 | 
					Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3<e.length?!1:1==e[0]&&0<=e[1]&&5>=e[1]?!0:0!=e[0]||10<
 | 
				
			||||||
 | 
					e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,
 | 
				
			||||||
 | 
					b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(ta||fa)fa?U=self.location.href:"undefined"!=typeof document&&document.currentScript&&(U=document.currentScript.src),h&&(U=h),U=0!==U.indexOf("blob:")?U.substr(0,U.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Wa=function(e){var b=new XMLHttpRequest;b.open("GET",
 | 
				
			||||||
 | 
					e,!1);b.send(null);return b.responseText},fa&&(ma=function(e){var b=new XMLHttpRequest;b.open("GET",e,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),na=function(e,b,c){var d=new XMLHttpRequest;d.open("GET",e,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var da=a.printErr||console.warn.bind(console);Object.assign(a,Ta);Ta=null;var ea;a.wasmBinary&&
 | 
				
			||||||
 | 
					(ea=a.wasmBinary);"object"!=typeof WebAssembly&&y("no native wasm support detected");var ja,sa=!1,ra="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,W,ia,ca,Y,ya=[],oa=[],xa=[],va=!1,ba=0,qa=null,ha=null;var P="draco_decoder_gltf.wasm";P.startsWith("data:application/octet-stream;base64,")||(P=k(P));var pd=0,qd={b:function(e,b,c){(new A(e)).init(b,c);pd++;throw e;},a:function(){y("")},d:function(e,b,c){ia.copyWithin(e,b,b+c)},c:function(e){var b=ia.length;e>>>=0;if(2147483648<e)return!1;
 | 
				
			||||||
 | 
					for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)}
 | 
				
			||||||
 | 
					function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||
 | 
				
			||||||
 | 
					P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=
 | 
				
			||||||
 | 
					function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=
 | 
				
			||||||
 | 
					a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=
 | 
				
			||||||
 | 
					a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=
 | 
				
			||||||
 | 
					function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0=
 | 
				
			||||||
 | 
					a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0=
 | 
				
			||||||
 | 
					function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=
 | 
				
			||||||
 | 
					function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb=
 | 
				
			||||||
 | 
					a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=
 | 
				
			||||||
 | 
					a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb=
 | 
				
			||||||
 | 
					a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null,
 | 
				
			||||||
 | 
					arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0=
 | 
				
			||||||
 | 
					a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0=
 | 
				
			||||||
 | 
					a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0=
 | 
				
			||||||
 | 
					a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib=
 | 
				
			||||||
 | 
					a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0=
 | 
				
			||||||
 | 
					function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=
 | 
				
			||||||
 | 
					a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=
 | 
				
			||||||
 | 
					function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=
 | 
				
			||||||
 | 
					a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=
 | 
				
			||||||
 | 
					function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0=
 | 
				
			||||||
 | 
					a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0=
 | 
				
			||||||
 | 
					function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=
 | 
				
			||||||
 | 
					a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null,
 | 
				
			||||||
 | 
					arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0=
 | 
				
			||||||
 | 
					function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2=
 | 
				
			||||||
 | 
					a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)},
 | 
				
			||||||
 | 
					oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=
 | 
				
			||||||
 | 
					function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc=
 | 
				
			||||||
 | 
					a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=
 | 
				
			||||||
 | 
					a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=
 | 
				
			||||||
 | 
					a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=
 | 
				
			||||||
 | 
					a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=
 | 
				
			||||||
 | 
					a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=
 | 
				
			||||||
 | 
					function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=
 | 
				
			||||||
 | 
					a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
 | 
				
			||||||
 | 
					a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=
 | 
				
			||||||
 | 
					a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=
 | 
				
			||||||
 | 
					a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc=
 | 
				
			||||||
 | 
					a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32=
 | 
				
			||||||
 | 
					a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=
 | 
				
			||||||
 | 
					function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=
 | 
				
			||||||
 | 
					a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=
 | 
				
			||||||
 | 
					function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)};
 | 
				
			||||||
 | 
					a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();F();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=w;a.wrapPointer=B;a.castObject=function(b,
 | 
				
			||||||
 | 
					c){return B(b.ptr,c)};a.NULL=B(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete w(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var r={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(r.needed){for(var b=0;b<r.temps.length;b++)a._free(r.temps[b]);r.temps.length=0;a._free(r.buffer);r.buffer=0;r.size+=
 | 
				
			||||||
 | 
					r.needed;r.needed=0}r.buffer||(r.size+=128,r.buffer=a._malloc(r.size),r.buffer||y(void 0));r.pos=0},alloc:function(b,c){r.buffer||y(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;r.pos+b>=r.size?(0<b||y(void 0),r.needed+=b,c=a._malloc(b),r.temps.push(c)):(c=r.buffer+r.pos,r.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;g<b.length;g++)c[d+g]=b[g]}};X.prototype=Object.create(v.prototype);X.prototype.constructor=
 | 
				
			||||||
 | 
					X;X.prototype.__class__=X;X.__cache__={};a.VoidPtr=X;X.prototype.__destroy__=X.prototype.__destroy__=function(){Xa(this.ptr)};S.prototype=Object.create(v.prototype);S.prototype.constructor=S;S.prototype.__class__=S;S.__cache__={};a.DecoderBuffer=S;S.prototype.Init=S.prototype.Init=function(b,c){var d=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);Ya(d,b,c)};S.prototype.__destroy__=S.prototype.__destroy__=function(){Za(this.ptr)};Q.prototype=Object.create(v.prototype);
 | 
				
			||||||
 | 
					Q.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.AttributeTransformData=Q;Q.prototype.transform_type=Q.prototype.transform_type=function(){return $a(this.ptr)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){ab(this.ptr)};V.prototype=Object.create(v.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){bb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=
 | 
				
			||||||
 | 
					x;x.prototype.__class__=x;x.__cache__={};a.PointAttribute=x;x.prototype.size=x.prototype.size=function(){return cb(this.ptr)};x.prototype.GetAttributeTransformData=x.prototype.GetAttributeTransformData=function(){return B(db(this.ptr),Q)};x.prototype.attribute_type=x.prototype.attribute_type=function(){return eb(this.ptr)};x.prototype.data_type=x.prototype.data_type=function(){return fb(this.ptr)};x.prototype.num_components=x.prototype.num_components=function(){return gb(this.ptr)};x.prototype.normalized=
 | 
				
			||||||
 | 
					x.prototype.normalized=function(){return!!hb(this.ptr)};x.prototype.byte_stride=x.prototype.byte_stride=function(){return ib(this.ptr)};x.prototype.byte_offset=x.prototype.byte_offset=function(){return jb(this.ptr)};x.prototype.unique_id=x.prototype.unique_id=function(){return kb(this.ptr)};x.prototype.__destroy__=x.prototype.__destroy__=function(){lb(this.ptr)};D.prototype=Object.create(v.prototype);D.prototype.constructor=D;D.prototype.__class__=D;D.__cache__={};a.AttributeQuantizationTransform=
 | 
				
			||||||
 | 
					D;D.prototype.InitFromAttribute=D.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!mb(c,b)};D.prototype.quantization_bits=D.prototype.quantization_bits=function(){return nb(this.ptr)};D.prototype.min_value=D.prototype.min_value=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return ob(c,b)};D.prototype.range=D.prototype.range=function(){return pb(this.ptr)};D.prototype.__destroy__=D.prototype.__destroy__=function(){qb(this.ptr)};G.prototype=
 | 
				
			||||||
 | 
					Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.AttributeOctahedronTransform=G;G.prototype.InitFromAttribute=G.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!rb(c,b)};G.prototype.quantization_bits=G.prototype.quantization_bits=function(){return sb(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){tb(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=
 | 
				
			||||||
 | 
					H;H.__cache__={};a.PointCloud=H;H.prototype.num_attributes=H.prototype.num_attributes=function(){return ub(this.ptr)};H.prototype.num_points=H.prototype.num_points=function(){return vb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){wb(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return xb(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
 | 
				
			||||||
 | 
					function(){return yb(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return zb(this.ptr)};E.prototype.__destroy__=E.prototype.__destroy__=function(){Ab(this.ptr)};T.prototype=Object.create(v.prototype);T.prototype.constructor=T;T.prototype.__class__=T;T.__cache__={};a.Metadata=T;T.prototype.__destroy__=T.prototype.__destroy__=function(){Bb(this.ptr)};C.prototype=Object.create(v.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Status=C;C.prototype.code=
 | 
				
			||||||
 | 
					C.prototype.code=function(){return Cb(this.ptr)};C.prototype.ok=C.prototype.ok=function(){return!!Db(this.ptr)};C.prototype.error_msg=C.prototype.error_msg=function(){return p(Eb(this.ptr))};C.prototype.__destroy__=C.prototype.__destroy__=function(){Fb(this.ptr)};I.prototype=Object.create(v.prototype);I.prototype.constructor=I;I.prototype.__class__=I;I.__cache__={};a.DracoFloat32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gb(c,
 | 
				
			||||||
 | 
					b)};I.prototype.size=I.prototype.size=function(){return Hb(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){Ib(this.ptr)};J.prototype=Object.create(v.prototype);J.prototype.constructor=J;J.prototype.__class__=J;J.__cache__={};a.DracoInt8Array=J;J.prototype.GetValue=J.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Jb(c,b)};J.prototype.size=J.prototype.size=function(){return Kb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){Lb(this.ptr)};
 | 
				
			||||||
 | 
					K.prototype=Object.create(v.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoUInt8Array=K;K.prototype.GetValue=K.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Mb(c,b)};K.prototype.size=K.prototype.size=function(){return Nb(this.ptr)};K.prototype.__destroy__=K.prototype.__destroy__=function(){Ob(this.ptr)};L.prototype=Object.create(v.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoInt16Array=
 | 
				
			||||||
 | 
					L;L.prototype.GetValue=L.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Pb(c,b)};L.prototype.size=L.prototype.size=function(){return Qb(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){Rb(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DracoUInt16Array=M;M.prototype.GetValue=M.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Sb(c,b)};
 | 
				
			||||||
 | 
					M.prototype.size=M.prototype.size=function(){return Tb(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=function(){Ub(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Vb(c,b)};N.prototype.size=N.prototype.size=function(){return Wb(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){Xb(this.ptr)};
 | 
				
			||||||
 | 
					O.prototype=Object.create(v.prototype);O.prototype.constructor=O;O.prototype.__class__=O;O.__cache__={};a.DracoUInt32Array=O;O.prototype.GetValue=O.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Yb(c,b)};O.prototype.size=O.prototype.size=function(){return Zb(this.ptr)};O.prototype.__destroy__=O.prototype.__destroy__=function(){$b(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.MetadataQuerier=
 | 
				
			||||||
 | 
					z;z.prototype.HasEntry=z.prototype.HasEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return!!ac(d,b,c)};z.prototype.GetIntEntry=z.prototype.GetIntEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return bc(d,b,c)};z.prototype.GetIntEntryArray=z.prototype.GetIntEntryArray=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===
 | 
				
			||||||
 | 
					typeof c?c.ptr:R(c);d&&"object"===typeof d&&(d=d.ptr);cc(g,b,c,d)};z.prototype.GetDoubleEntry=z.prototype.GetDoubleEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return dc(d,b,c)};z.prototype.GetStringEntry=z.prototype.GetStringEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return p(ec(d,b,c))};z.prototype.NumEntries=z.prototype.NumEntries=function(b){var c=this.ptr;
 | 
				
			||||||
 | 
					b&&"object"===typeof b&&(b=b.ptr);return fc(c,b)};z.prototype.GetEntryName=z.prototype.GetEntryName=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return p(gc(d,b,c))};z.prototype.__destroy__=z.prototype.__destroy__=function(){hc(this.ptr)};m.prototype=Object.create(v.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.Decoder=m;m.prototype.DecodeArrayToPointCloud=m.prototype.DecodeArrayToPointCloud=function(b,c,d){var g=
 | 
				
			||||||
 | 
					this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(ic(g,b,c,d),C)};m.prototype.DecodeArrayToMesh=m.prototype.DecodeArrayToMesh=function(b,c,d){var g=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(jc(g,b,c,d),C)};m.prototype.GetAttributeId=m.prototype.GetAttributeId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
 | 
				
			||||||
 | 
					(c=c.ptr);return kc(d,b,c)};m.prototype.GetAttributeIdByName=m.prototype.GetAttributeIdByName=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return lc(d,b,c)};m.prototype.GetAttributeIdByMetadataEntry=m.prototype.GetAttributeIdByMetadataEntry=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);d=d&&"object"===typeof d?d.ptr:R(d);return mc(g,b,c,d)};m.prototype.GetAttribute=
 | 
				
			||||||
 | 
					m.prototype.GetAttribute=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(nc(d,b,c),x)};m.prototype.GetAttributeByUniqueId=m.prototype.GetAttributeByUniqueId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(oc(d,b,c),x)};m.prototype.GetMetadata=m.prototype.GetMetadata=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return B(pc(c,b),T)};m.prototype.GetAttributeMetadata=m.prototype.GetAttributeMetadata=
 | 
				
			||||||
 | 
					function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(qc(d,b,c),T)};m.prototype.GetFaceFromMesh=m.prototype.GetFaceFromMesh=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!rc(g,b,c,d)};m.prototype.GetTriangleStripsFromMesh=m.prototype.GetTriangleStripsFromMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
 | 
				
			||||||
 | 
					return sc(d,b,c)};m.prototype.GetTrianglesUInt16Array=m.prototype.GetTrianglesUInt16Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!tc(g,b,c,d)};m.prototype.GetTrianglesUInt32Array=m.prototype.GetTrianglesUInt32Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!uc(g,b,c,d)};m.prototype.GetAttributeFloat=m.prototype.GetAttributeFloat=
 | 
				
			||||||
 | 
					function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vc(g,b,c,d)};m.prototype.GetAttributeFloatForAllPoints=m.prototype.GetAttributeFloatForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!wc(g,b,c,d)};m.prototype.GetAttributeIntForAllPoints=m.prototype.GetAttributeIntForAllPoints=function(b,c,d){var g=this.ptr;
 | 
				
			||||||
 | 
					b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!xc(g,b,c,d)};m.prototype.GetAttributeInt8ForAllPoints=m.prototype.GetAttributeInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!yc(g,b,c,d)};m.prototype.GetAttributeUInt8ForAllPoints=m.prototype.GetAttributeUInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=
 | 
				
			||||||
 | 
					b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!zc(g,b,c,d)};m.prototype.GetAttributeInt16ForAllPoints=m.prototype.GetAttributeInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ac(g,b,c,d)};m.prototype.GetAttributeUInt16ForAllPoints=m.prototype.GetAttributeUInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
 | 
				
			||||||
 | 
					(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Bc(g,b,c,d)};m.prototype.GetAttributeInt32ForAllPoints=m.prototype.GetAttributeInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Cc(g,b,c,d)};m.prototype.GetAttributeUInt32ForAllPoints=m.prototype.GetAttributeUInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
 | 
				
			||||||
 | 
					typeof d&&(d=d.ptr);return!!Dc(g,b,c,d)};m.prototype.GetAttributeDataArrayForAllPoints=m.prototype.GetAttributeDataArrayForAllPoints=function(b,c,d,g,t){var aa=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);g&&"object"===typeof g&&(g=g.ptr);t&&"object"===typeof t&&(t=t.ptr);return!!Ec(aa,b,c,d,g,t)};m.prototype.SkipAttributeTransform=m.prototype.SkipAttributeTransform=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Fc(c,
 | 
				
			||||||
 | 
					b)};m.prototype.GetEncodedGeometryType_Deprecated=m.prototype.GetEncodedGeometryType_Deprecated=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gc(c,b)};m.prototype.DecodeBufferToPointCloud=m.prototype.DecodeBufferToPointCloud=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(Hc(d,b,c),C)};m.prototype.DecodeBufferToMesh=m.prototype.DecodeBufferToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
 | 
				
			||||||
 | 
					typeof c&&(c=c.ptr);return B(Ic(d,b,c),C)};m.prototype.__destroy__=m.prototype.__destroy__=function(){Jc(this.ptr)};(function(){function b(){a.ATTRIBUTE_INVALID_TRANSFORM=Kc();a.ATTRIBUTE_NO_TRANSFORM=Lc();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Mc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Nc();a.INVALID=Oc();a.POSITION=Pc();a.NORMAL=Qc();a.COLOR=Rc();a.TEX_COORD=Sc();a.GENERIC=Tc();a.INVALID_GEOMETRY_TYPE=Uc();a.POINT_CLOUD=Vc();a.TRIANGULAR_MESH=Wc();a.DT_INVALID=Xc();a.DT_INT8=Yc();a.DT_UINT8=Zc();a.DT_INT16=
 | 
				
			||||||
 | 
					$c();a.DT_UINT16=ad();a.DT_INT32=bd();a.DT_UINT32=cd();a.DT_INT64=dd();a.DT_UINT64=ed();a.DT_FLOAT32=fd();a.DT_FLOAT64=gd();a.DT_BOOL=hd();a.DT_TYPES_COUNT=id();a.OK=jd();a.DRACO_ERROR=kd();a.IO_ERROR=ld();a.INVALID_PARAMETER=md();a.UNSUPPORTED_VERSION=nd();a.UNKNOWN_VERSION=od()}va?b():oa.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();a.Decoder.prototype.GetEncodedGeometryType=function(b){if(b.__class__&&b.__class__===a.DecoderBuffer)return a.Decoder.prototype.GetEncodedGeometryType_Deprecated(b);
 | 
				
			||||||
 | 
					if(8>b.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);
 | 
				
			||||||
							
								
								
									
										21
									
								
								public/bblcdn/msc_basis_transcoder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/bblcdn/msc_basis_transcoder.wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/bblcdn/uastc_bc7.wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/bblcdn/zstddec.wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/hdr/hdr.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/model/skateboard_mesh.glb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/model/t-shirt.glb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										21
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <v-app>
 | 
				
			||||||
 | 
					    <router-view />
 | 
				
			||||||
 | 
					  </v-app>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					// 移除直接导入MainApp,改用路由系统
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					#app {
 | 
				
			||||||
 | 
					  width: 100vw;
 | 
				
			||||||
 | 
					  height: 100vh;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/font/QingNiaoHuaGuangJianMeiHei-2.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/a25614bc973f340740cb229104f29eb.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 278 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/bangzhu1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 134 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/bangzhu2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 122 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/btn_产品卖点背景.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/btn_外观页_底背景条.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 168 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/logo.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 586 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/img/微信图片_20250512185835.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 516 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/logo.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 586 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.7 KiB  | 
							
								
								
									
										6
									
								
								src/assets/logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					<path d="M261.126 140.65L164.624 307.732L256.001 466L377.028 256.5L498.001 47H315.192L261.126 140.65Z" fill="#1697F6"/>
 | 
				
			||||||
 | 
					<path d="M135.027 256.5L141.365 267.518L231.64 111.178L268.731 47H256H14L135.027 256.5Z" fill="#AEDDFF"/>
 | 
				
			||||||
 | 
					<path d="M315.191 47C360.935 197.446 256 466 256 466L164.624 307.732L315.191 47Z" fill="#1867C0"/>
 | 
				
			||||||
 | 
					<path d="M268.731 47C76.0026 47 141.366 267.518 141.366 267.518L268.731 47Z" fill="#7BC6FF"/>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 526 B  | 
							
								
								
									
										46
									
								
								src/assets/style/components.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					.inspectortotip {
 | 
				
			||||||
 | 
					    margin-top: 5px;
 | 
				
			||||||
 | 
					    color: rgba(250, 92, 0, 0.712);
 | 
				
			||||||
 | 
					    width: 200px;
 | 
				
			||||||
 | 
					    margin-left: 20px;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    margin-bottom: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.container {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    pointer-events: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.slider-them {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    pointer-events: auto;
 | 
				
			||||||
 | 
					    cursor: ew-resize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    width: 20px;
 | 
				
			||||||
 | 
					    height: 40px;
 | 
				
			||||||
 | 
					    background-color: #006eff;
 | 
				
			||||||
 | 
					    opacity: 0.7;
 | 
				
			||||||
 | 
					    border-radius: 1rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    top: calc(50% - 20px);
 | 
				
			||||||
 | 
					    left: calc(50% - 10px);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.slider {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    pointer-events: auto;
 | 
				
			||||||
 | 
					    cursor: ew-resize;
 | 
				
			||||||
 | 
					    width: 40px;
 | 
				
			||||||
 | 
					    height: 60px;
 | 
				
			||||||
 | 
					    background-color: #7777773a;
 | 
				
			||||||
 | 
					    opacity: 0.7;
 | 
				
			||||||
 | 
					    border-radius: 1rem;
 | 
				
			||||||
 | 
					    top: calc(50% - 20px);
 | 
				
			||||||
 | 
					    left: calc(50% - 10px);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										539
									
								
								src/assets/style/font_oy0t1irww4k/demo.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,539 @@
 | 
				
			|||||||
 | 
					/* Logo 字体 */
 | 
				
			||||||
 | 
					@font-face {
 | 
				
			||||||
 | 
					  font-family: "iconfont logo";
 | 
				
			||||||
 | 
					  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
 | 
				
			||||||
 | 
					  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
 | 
				
			||||||
 | 
					    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
 | 
				
			||||||
 | 
					    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
 | 
				
			||||||
 | 
					    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.logo {
 | 
				
			||||||
 | 
					  font-family: "iconfont logo";
 | 
				
			||||||
 | 
					  font-size: 160px;
 | 
				
			||||||
 | 
					  font-style: normal;
 | 
				
			||||||
 | 
					  -webkit-font-smoothing: antialiased;
 | 
				
			||||||
 | 
					  -moz-osx-font-smoothing: grayscale;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* tabs */
 | 
				
			||||||
 | 
					.nav-tabs {
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-tabs .nav-more {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  right: 0;
 | 
				
			||||||
 | 
					  bottom: 0;
 | 
				
			||||||
 | 
					  height: 42px;
 | 
				
			||||||
 | 
					  line-height: 42px;
 | 
				
			||||||
 | 
					  color: #666;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#tabs {
 | 
				
			||||||
 | 
					  border-bottom: 1px solid #eee;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#tabs li {
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  width: 100px;
 | 
				
			||||||
 | 
					  height: 40px;
 | 
				
			||||||
 | 
					  line-height: 40px;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					  border-bottom: 2px solid transparent;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  z-index: 1;
 | 
				
			||||||
 | 
					  margin-bottom: -1px;
 | 
				
			||||||
 | 
					  color: #666;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#tabs .active {
 | 
				
			||||||
 | 
					  border-bottom-color: #f00;
 | 
				
			||||||
 | 
					  color: #222;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.tab-container .content {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 页面布局 */
 | 
				
			||||||
 | 
					.main {
 | 
				
			||||||
 | 
					  padding: 30px 100px;
 | 
				
			||||||
 | 
					  width: 960px;
 | 
				
			||||||
 | 
					  margin: 0 auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.main .logo {
 | 
				
			||||||
 | 
					  color: #333;
 | 
				
			||||||
 | 
					  text-align: left;
 | 
				
			||||||
 | 
					  margin-bottom: 30px;
 | 
				
			||||||
 | 
					  line-height: 1;
 | 
				
			||||||
 | 
					  height: 110px;
 | 
				
			||||||
 | 
					  margin-top: -50px;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  *zoom: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.main .logo a {
 | 
				
			||||||
 | 
					  font-size: 160px;
 | 
				
			||||||
 | 
					  color: #333;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.helps {
 | 
				
			||||||
 | 
					  margin-top: 40px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.helps pre {
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					  margin: 10px 0;
 | 
				
			||||||
 | 
					  border: solid 1px #e7e1cd;
 | 
				
			||||||
 | 
					  background-color: #fffdef;
 | 
				
			||||||
 | 
					  overflow: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon_lists {
 | 
				
			||||||
 | 
					  width: 100% !important;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  *zoom: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon_lists li {
 | 
				
			||||||
 | 
					  width: 100px;
 | 
				
			||||||
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
 | 
					  margin-right: 20px;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  list-style: none !important;
 | 
				
			||||||
 | 
					  cursor: default;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon_lists li .code-name {
 | 
				
			||||||
 | 
					  line-height: 1.2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon_lists .icon {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  height: 100px;
 | 
				
			||||||
 | 
					  line-height: 100px;
 | 
				
			||||||
 | 
					  font-size: 42px;
 | 
				
			||||||
 | 
					  margin: 10px auto;
 | 
				
			||||||
 | 
					  color: #333;
 | 
				
			||||||
 | 
					  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
 | 
				
			||||||
 | 
					  -moz-transition: font-size 0.25s linear, width 0.25s linear;
 | 
				
			||||||
 | 
					  transition: font-size 0.25s linear, width 0.25s linear;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon_lists .icon:hover {
 | 
				
			||||||
 | 
					  font-size: 100px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon_lists .svg-icon {
 | 
				
			||||||
 | 
					  /* 通过设置 font-size 来改变图标大小 */
 | 
				
			||||||
 | 
					  width: 1em;
 | 
				
			||||||
 | 
					  /* 图标和文字相邻时,垂直对齐 */
 | 
				
			||||||
 | 
					  vertical-align: -0.15em;
 | 
				
			||||||
 | 
					  /* 通过设置 color 来改变 SVG 的颜色/fill */
 | 
				
			||||||
 | 
					  fill: currentColor;
 | 
				
			||||||
 | 
					  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
 | 
				
			||||||
 | 
					      normalize.css 中也包含这行 */
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon_lists li .name,
 | 
				
			||||||
 | 
					.icon_lists li .code-name {
 | 
				
			||||||
 | 
					  color: #666;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* markdown 样式 */
 | 
				
			||||||
 | 
					.markdown {
 | 
				
			||||||
 | 
					  color: #666;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  line-height: 1.8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.highlight {
 | 
				
			||||||
 | 
					  line-height: 1.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown img {
 | 
				
			||||||
 | 
					  vertical-align: middle;
 | 
				
			||||||
 | 
					  max-width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h1 {
 | 
				
			||||||
 | 
					  color: #404040;
 | 
				
			||||||
 | 
					  font-weight: 500;
 | 
				
			||||||
 | 
					  line-height: 40px;
 | 
				
			||||||
 | 
					  margin-bottom: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h2,
 | 
				
			||||||
 | 
					.markdown h3,
 | 
				
			||||||
 | 
					.markdown h4,
 | 
				
			||||||
 | 
					.markdown h5,
 | 
				
			||||||
 | 
					.markdown h6 {
 | 
				
			||||||
 | 
					  color: #404040;
 | 
				
			||||||
 | 
					  margin: 1.6em 0 0.6em 0;
 | 
				
			||||||
 | 
					  font-weight: 500;
 | 
				
			||||||
 | 
					  clear: both;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h1 {
 | 
				
			||||||
 | 
					  font-size: 28px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h2 {
 | 
				
			||||||
 | 
					  font-size: 22px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h3 {
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h4 {
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h5 {
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h6 {
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown hr {
 | 
				
			||||||
 | 
					  height: 1px;
 | 
				
			||||||
 | 
					  border: 0;
 | 
				
			||||||
 | 
					  background: #e9e9e9;
 | 
				
			||||||
 | 
					  margin: 16px 0;
 | 
				
			||||||
 | 
					  clear: both;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown p {
 | 
				
			||||||
 | 
					  margin: 1em 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>p,
 | 
				
			||||||
 | 
					.markdown>blockquote,
 | 
				
			||||||
 | 
					.markdown>.highlight,
 | 
				
			||||||
 | 
					.markdown>ol,
 | 
				
			||||||
 | 
					.markdown>ul {
 | 
				
			||||||
 | 
					  width: 80%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown ul>li {
 | 
				
			||||||
 | 
					  list-style: circle;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>ul li,
 | 
				
			||||||
 | 
					.markdown blockquote ul>li {
 | 
				
			||||||
 | 
					  margin-left: 20px;
 | 
				
			||||||
 | 
					  padding-left: 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>ul li p,
 | 
				
			||||||
 | 
					.markdown>ol li p {
 | 
				
			||||||
 | 
					  margin: 0.6em 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown ol>li {
 | 
				
			||||||
 | 
					  list-style: decimal;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>ol li,
 | 
				
			||||||
 | 
					.markdown blockquote ol>li {
 | 
				
			||||||
 | 
					  margin-left: 20px;
 | 
				
			||||||
 | 
					  padding-left: 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown code {
 | 
				
			||||||
 | 
					  margin: 0 3px;
 | 
				
			||||||
 | 
					  padding: 0 5px;
 | 
				
			||||||
 | 
					  background: #eee;
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown strong,
 | 
				
			||||||
 | 
					.markdown b {
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>table {
 | 
				
			||||||
 | 
					  border-collapse: collapse;
 | 
				
			||||||
 | 
					  border-spacing: 0px;
 | 
				
			||||||
 | 
					  empty-cells: show;
 | 
				
			||||||
 | 
					  border: 1px solid #e9e9e9;
 | 
				
			||||||
 | 
					  width: 95%;
 | 
				
			||||||
 | 
					  margin-bottom: 24px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>table th {
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  color: #333;
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>table th,
 | 
				
			||||||
 | 
					.markdown>table td {
 | 
				
			||||||
 | 
					  border: 1px solid #e9e9e9;
 | 
				
			||||||
 | 
					  padding: 8px 16px;
 | 
				
			||||||
 | 
					  text-align: left;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>table th {
 | 
				
			||||||
 | 
					  background: #F7F7F7;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown blockquote {
 | 
				
			||||||
 | 
					  font-size: 90%;
 | 
				
			||||||
 | 
					  color: #999;
 | 
				
			||||||
 | 
					  border-left: 4px solid #e9e9e9;
 | 
				
			||||||
 | 
					  padding-left: 0.8em;
 | 
				
			||||||
 | 
					  margin: 1em 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown blockquote p {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown .anchor {
 | 
				
			||||||
 | 
					  opacity: 0;
 | 
				
			||||||
 | 
					  transition: opacity 0.3s ease;
 | 
				
			||||||
 | 
					  margin-left: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown .waiting {
 | 
				
			||||||
 | 
					  color: #ccc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown h1:hover .anchor,
 | 
				
			||||||
 | 
					.markdown h2:hover .anchor,
 | 
				
			||||||
 | 
					.markdown h3:hover .anchor,
 | 
				
			||||||
 | 
					.markdown h4:hover .anchor,
 | 
				
			||||||
 | 
					.markdown h5:hover .anchor,
 | 
				
			||||||
 | 
					.markdown h6:hover .anchor {
 | 
				
			||||||
 | 
					  opacity: 1;
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markdown>br,
 | 
				
			||||||
 | 
					.markdown>p>br {
 | 
				
			||||||
 | 
					  clear: both;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  background: white;
 | 
				
			||||||
 | 
					  padding: 0.5em;
 | 
				
			||||||
 | 
					  color: #333333;
 | 
				
			||||||
 | 
					  overflow-x: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-comment,
 | 
				
			||||||
 | 
					.hljs-meta {
 | 
				
			||||||
 | 
					  color: #969896;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-string,
 | 
				
			||||||
 | 
					.hljs-variable,
 | 
				
			||||||
 | 
					.hljs-template-variable,
 | 
				
			||||||
 | 
					.hljs-strong,
 | 
				
			||||||
 | 
					.hljs-emphasis,
 | 
				
			||||||
 | 
					.hljs-quote {
 | 
				
			||||||
 | 
					  color: #df5000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-keyword,
 | 
				
			||||||
 | 
					.hljs-selector-tag,
 | 
				
			||||||
 | 
					.hljs-type {
 | 
				
			||||||
 | 
					  color: #a71d5d;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-literal,
 | 
				
			||||||
 | 
					.hljs-symbol,
 | 
				
			||||||
 | 
					.hljs-bullet,
 | 
				
			||||||
 | 
					.hljs-attribute {
 | 
				
			||||||
 | 
					  color: #0086b3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-section,
 | 
				
			||||||
 | 
					.hljs-name {
 | 
				
			||||||
 | 
					  color: #63a35c;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-tag {
 | 
				
			||||||
 | 
					  color: #333333;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-title,
 | 
				
			||||||
 | 
					.hljs-attr,
 | 
				
			||||||
 | 
					.hljs-selector-id,
 | 
				
			||||||
 | 
					.hljs-selector-class,
 | 
				
			||||||
 | 
					.hljs-selector-attr,
 | 
				
			||||||
 | 
					.hljs-selector-pseudo {
 | 
				
			||||||
 | 
					  color: #795da3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-addition {
 | 
				
			||||||
 | 
					  color: #55a532;
 | 
				
			||||||
 | 
					  background-color: #eaffea;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-deletion {
 | 
				
			||||||
 | 
					  color: #bd2c00;
 | 
				
			||||||
 | 
					  background-color: #ffecec;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hljs-link {
 | 
				
			||||||
 | 
					  text-decoration: underline;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 代码高亮 */
 | 
				
			||||||
 | 
					/* PrismJS 1.15.0
 | 
				
			||||||
 | 
					https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * prism.js default theme for JavaScript, CSS and HTML
 | 
				
			||||||
 | 
					 * Based on dabblet (http://dabblet.com)
 | 
				
			||||||
 | 
					 * @author Lea Verou
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					code[class*="language-"],
 | 
				
			||||||
 | 
					pre[class*="language-"] {
 | 
				
			||||||
 | 
					  color: black;
 | 
				
			||||||
 | 
					  background: none;
 | 
				
			||||||
 | 
					  text-shadow: 0 1px white;
 | 
				
			||||||
 | 
					  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
 | 
				
			||||||
 | 
					  text-align: left;
 | 
				
			||||||
 | 
					  white-space: pre;
 | 
				
			||||||
 | 
					  word-spacing: normal;
 | 
				
			||||||
 | 
					  word-break: normal;
 | 
				
			||||||
 | 
					  word-wrap: normal;
 | 
				
			||||||
 | 
					  line-height: 1.5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -moz-tab-size: 4;
 | 
				
			||||||
 | 
					  -o-tab-size: 4;
 | 
				
			||||||
 | 
					  tab-size: 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -webkit-hyphens: none;
 | 
				
			||||||
 | 
					  -moz-hyphens: none;
 | 
				
			||||||
 | 
					  -ms-hyphens: none;
 | 
				
			||||||
 | 
					  hyphens: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pre[class*="language-"]::-moz-selection,
 | 
				
			||||||
 | 
					pre[class*="language-"] ::-moz-selection,
 | 
				
			||||||
 | 
					code[class*="language-"]::-moz-selection,
 | 
				
			||||||
 | 
					code[class*="language-"] ::-moz-selection {
 | 
				
			||||||
 | 
					  text-shadow: none;
 | 
				
			||||||
 | 
					  background: #b3d4fc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pre[class*="language-"]::selection,
 | 
				
			||||||
 | 
					pre[class*="language-"] ::selection,
 | 
				
			||||||
 | 
					code[class*="language-"]::selection,
 | 
				
			||||||
 | 
					code[class*="language-"] ::selection {
 | 
				
			||||||
 | 
					  text-shadow: none;
 | 
				
			||||||
 | 
					  background: #b3d4fc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media print {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  code[class*="language-"],
 | 
				
			||||||
 | 
					  pre[class*="language-"] {
 | 
				
			||||||
 | 
					    text-shadow: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Code blocks */
 | 
				
			||||||
 | 
					pre[class*="language-"] {
 | 
				
			||||||
 | 
					  padding: 1em;
 | 
				
			||||||
 | 
					  margin: .5em 0;
 | 
				
			||||||
 | 
					  overflow: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:not(pre)>code[class*="language-"],
 | 
				
			||||||
 | 
					pre[class*="language-"] {
 | 
				
			||||||
 | 
					  background: #f5f2f0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Inline code */
 | 
				
			||||||
 | 
					:not(pre)>code[class*="language-"] {
 | 
				
			||||||
 | 
					  padding: .1em;
 | 
				
			||||||
 | 
					  border-radius: .3em;
 | 
				
			||||||
 | 
					  white-space: normal;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.comment,
 | 
				
			||||||
 | 
					.token.prolog,
 | 
				
			||||||
 | 
					.token.doctype,
 | 
				
			||||||
 | 
					.token.cdata {
 | 
				
			||||||
 | 
					  color: slategray;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.punctuation {
 | 
				
			||||||
 | 
					  color: #999;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.namespace {
 | 
				
			||||||
 | 
					  opacity: .7;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.property,
 | 
				
			||||||
 | 
					.token.tag,
 | 
				
			||||||
 | 
					.token.boolean,
 | 
				
			||||||
 | 
					.token.number,
 | 
				
			||||||
 | 
					.token.constant,
 | 
				
			||||||
 | 
					.token.symbol,
 | 
				
			||||||
 | 
					.token.deleted {
 | 
				
			||||||
 | 
					  color: #905;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.selector,
 | 
				
			||||||
 | 
					.token.attr-name,
 | 
				
			||||||
 | 
					.token.string,
 | 
				
			||||||
 | 
					.token.char,
 | 
				
			||||||
 | 
					.token.builtin,
 | 
				
			||||||
 | 
					.token.inserted {
 | 
				
			||||||
 | 
					  color: #690;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.operator,
 | 
				
			||||||
 | 
					.token.entity,
 | 
				
			||||||
 | 
					.token.url,
 | 
				
			||||||
 | 
					.language-css .token.string,
 | 
				
			||||||
 | 
					.style .token.string {
 | 
				
			||||||
 | 
					  color: #9a6e3a;
 | 
				
			||||||
 | 
					  background: hsla(0, 0%, 100%, .5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.atrule,
 | 
				
			||||||
 | 
					.token.attr-value,
 | 
				
			||||||
 | 
					.token.keyword {
 | 
				
			||||||
 | 
					  color: #07a;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.function,
 | 
				
			||||||
 | 
					.token.class-name {
 | 
				
			||||||
 | 
					  color: #DD4A68;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.regex,
 | 
				
			||||||
 | 
					.token.important,
 | 
				
			||||||
 | 
					.token.variable {
 | 
				
			||||||
 | 
					  color: #e90;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.important,
 | 
				
			||||||
 | 
					.token.bold {
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.italic {
 | 
				
			||||||
 | 
					  font-style: italic;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token.entity {
 | 
				
			||||||
 | 
					  cursor: help;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										391
									
								
								src/assets/style/font_oy0t1irww4k/demo_index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,391 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					  <meta charset="utf-8"/>
 | 
				
			||||||
 | 
					  <title>iconfont Demo</title>
 | 
				
			||||||
 | 
					  <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
 | 
				
			||||||
 | 
					  <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="demo.css">
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="iconfont.css">
 | 
				
			||||||
 | 
					  <script src="iconfont.js"></script>
 | 
				
			||||||
 | 
					  <!-- jQuery -->
 | 
				
			||||||
 | 
					  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
 | 
				
			||||||
 | 
					  <!-- 代码高亮 -->
 | 
				
			||||||
 | 
					  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
 | 
				
			||||||
 | 
					  <style>
 | 
				
			||||||
 | 
					    .main .logo {
 | 
				
			||||||
 | 
					      margin-top: 0;
 | 
				
			||||||
 | 
					      height: auto;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .main .logo a {
 | 
				
			||||||
 | 
					      display: flex;
 | 
				
			||||||
 | 
					      align-items: center;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .main .logo .sub-title {
 | 
				
			||||||
 | 
					      margin-left: 0.5em;
 | 
				
			||||||
 | 
					      font-size: 22px;
 | 
				
			||||||
 | 
					      color: #fff;
 | 
				
			||||||
 | 
					      background: linear-gradient(-45deg, #3967FF, #B500FE);
 | 
				
			||||||
 | 
					      -webkit-background-clip: text;
 | 
				
			||||||
 | 
					      -webkit-text-fill-color: transparent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  </style>
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					  <div class="main">
 | 
				
			||||||
 | 
					    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
 | 
				
			||||||
 | 
					      <img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    </a></h1>
 | 
				
			||||||
 | 
					    <div class="nav-tabs">
 | 
				
			||||||
 | 
					      <ul id="tabs" class="dib-box">
 | 
				
			||||||
 | 
					        <li class="dib active"><span>Unicode</span></li>
 | 
				
			||||||
 | 
					        <li class="dib"><span>Font class</span></li>
 | 
				
			||||||
 | 
					        <li class="dib"><span>Symbol</span></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="tab-container">
 | 
				
			||||||
 | 
					      <div class="content unicode" style="display: block;">
 | 
				
			||||||
 | 
					          <ul class="icon_lists dib-box">
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">全屏</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe660;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">帮助</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe620;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">帮助_o</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xeb72;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">半透明(2)</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe60b;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">持续时间</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe71a;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">对比</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe60c;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">还原</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe604;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">vr</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe70a;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					              <span class="icon iconfont"></span>
 | 
				
			||||||
 | 
					                <div class="name">持续时间</div>
 | 
				
			||||||
 | 
					                <div class="code-name">&#xe51b;</div>
 | 
				
			||||||
 | 
					              </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          </ul>
 | 
				
			||||||
 | 
					          <div class="article markdown">
 | 
				
			||||||
 | 
					          <h2 id="unicode-">Unicode 引用</h2>
 | 
				
			||||||
 | 
					          <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
 | 
				
			||||||
 | 
					          <ul>
 | 
				
			||||||
 | 
					            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
 | 
				
			||||||
 | 
					            <li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
 | 
				
			||||||
 | 
					          </ul>
 | 
				
			||||||
 | 
					          <blockquote>
 | 
				
			||||||
 | 
					            <p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
 | 
				
			||||||
 | 
					          </blockquote>
 | 
				
			||||||
 | 
					          <p>Unicode 使用步骤如下:</p>
 | 
				
			||||||
 | 
					          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
 | 
				
			||||||
 | 
					<pre><code class="language-css"
 | 
				
			||||||
 | 
					>@font-face {
 | 
				
			||||||
 | 
					  font-family: 'iconfont';
 | 
				
			||||||
 | 
					  src: url('iconfont.ttf?t=1733881413198') format('truetype');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
 | 
				
			||||||
 | 
					<pre><code class="language-css"
 | 
				
			||||||
 | 
					>.iconfont {
 | 
				
			||||||
 | 
					  font-family: "iconfont" !important;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					  font-style: normal;
 | 
				
			||||||
 | 
					  -webkit-font-smoothing: antialiased;
 | 
				
			||||||
 | 
					  -moz-osx-font-smoothing: grayscale;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
 | 
				
			||||||
 | 
					<pre>
 | 
				
			||||||
 | 
					<code class="language-html"
 | 
				
			||||||
 | 
					><span class="iconfont">&#x33;</span>
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					          <blockquote>
 | 
				
			||||||
 | 
					            <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
 | 
				
			||||||
 | 
					          </blockquote>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="content font-class">
 | 
				
			||||||
 | 
					        <ul class="icon_lists dib-box">
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-quanping"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              全屏
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-quanping
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-bangzhu"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              帮助
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-bangzhu
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-bangzhu_o"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              帮助_o
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-bangzhu_o
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-bantouming"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              半透明(2)
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-bantouming
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-chixushijian"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              持续时间
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-chixushijian
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-duibi"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              对比
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-duibi
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-huanyuan"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              还原
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-huanyuan
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-vr"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              vr
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-vr
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          <li class="dib">
 | 
				
			||||||
 | 
					            <span class="icon iconfont icon-chixushijian1"></span>
 | 
				
			||||||
 | 
					            <div class="name">
 | 
				
			||||||
 | 
					              持续时间
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="code-name">.icon-chixushijian1
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					        <div class="article markdown">
 | 
				
			||||||
 | 
					        <h2 id="font-class-">font-class 引用</h2>
 | 
				
			||||||
 | 
					        <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
 | 
				
			||||||
 | 
					        <p>与 Unicode 使用方式相比,具有如下特点:</p>
 | 
				
			||||||
 | 
					        <ul>
 | 
				
			||||||
 | 
					          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
 | 
				
			||||||
 | 
					          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					        <p>使用步骤如下:</p>
 | 
				
			||||||
 | 
					        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
 | 
				
			||||||
 | 
					<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
 | 
				
			||||||
 | 
					<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					        <blockquote>
 | 
				
			||||||
 | 
					          <p>"
 | 
				
			||||||
 | 
					            iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
 | 
				
			||||||
 | 
					        </blockquote>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="content symbol">
 | 
				
			||||||
 | 
					          <ul class="icon_lists dib-box">
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-quanping"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">全屏</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-quanping</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-bangzhu"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">帮助</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-bangzhu</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-bangzhu_o"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">帮助_o</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-bangzhu_o</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-bantouming"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">半透明(2)</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-bantouming</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-chixushijian"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">持续时间</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-chixushijian</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-duibi"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">对比</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-duibi</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-huanyuan"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">还原</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-huanyuan</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-vr"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">vr</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-vr</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					            <li class="dib">
 | 
				
			||||||
 | 
					                <svg class="icon svg-icon" aria-hidden="true">
 | 
				
			||||||
 | 
					                  <use xlink:href="#icon-chixushijian1"></use>
 | 
				
			||||||
 | 
					                </svg>
 | 
				
			||||||
 | 
					                <div class="name">持续时间</div>
 | 
				
			||||||
 | 
					                <div class="code-name">#icon-chixushijian1</div>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          </ul>
 | 
				
			||||||
 | 
					          <div class="article markdown">
 | 
				
			||||||
 | 
					          <h2 id="symbol-">Symbol 引用</h2>
 | 
				
			||||||
 | 
					          <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
 | 
				
			||||||
 | 
					            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
 | 
				
			||||||
 | 
					          <ul>
 | 
				
			||||||
 | 
					            <li>支持多色图标了,不再受单色限制。</li>
 | 
				
			||||||
 | 
					            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
 | 
				
			||||||
 | 
					            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
 | 
				
			||||||
 | 
					            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
 | 
				
			||||||
 | 
					          </ul>
 | 
				
			||||||
 | 
					          <p>使用步骤如下:</p>
 | 
				
			||||||
 | 
					          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
 | 
				
			||||||
 | 
					<pre><code class="language-html"><script src="./iconfont.js"></script>
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
 | 
				
			||||||
 | 
					<pre><code class="language-html"><style>
 | 
				
			||||||
 | 
					.icon {
 | 
				
			||||||
 | 
					  width: 1em;
 | 
				
			||||||
 | 
					  height: 1em;
 | 
				
			||||||
 | 
					  vertical-align: -0.15em;
 | 
				
			||||||
 | 
					  fill: currentColor;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
 | 
				
			||||||
 | 
					<pre><code class="language-html"><svg class="icon" aria-hidden="true">
 | 
				
			||||||
 | 
					  <use xlink:href="#icon-xxx"></use>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
 | 
					</code></pre>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <script>
 | 
				
			||||||
 | 
					  $(document).ready(function () {
 | 
				
			||||||
 | 
					      $('.tab-container .content:first').show()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      $('#tabs li').click(function (e) {
 | 
				
			||||||
 | 
					        var tabContent = $('.tab-container .content')
 | 
				
			||||||
 | 
					        var index = $(this).index()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($(this).hasClass('active')) {
 | 
				
			||||||
 | 
					          return
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          $('#tabs li').removeClass('active')
 | 
				
			||||||
 | 
					          $(this).addClass('active')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          tabContent.hide().eq(index).fadeIn()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  </script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										49
									
								
								src/assets/style/font_oy0t1irww4k/iconfont.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					@font-face {
 | 
				
			||||||
 | 
					  font-family: "iconfont"; /* Project id  */
 | 
				
			||||||
 | 
					  src: url('iconfont.ttf?t=1733881413198') format('truetype');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.iconfont {
 | 
				
			||||||
 | 
					  font-family: "iconfont" !important;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					  font-style: normal;
 | 
				
			||||||
 | 
					  -webkit-font-smoothing: antialiased;
 | 
				
			||||||
 | 
					  -moz-osx-font-smoothing: grayscale;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-quanping:before {
 | 
				
			||||||
 | 
					  content: "\e660";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-bangzhu:before {
 | 
				
			||||||
 | 
					  content: "\e620";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-bangzhu_o:before {
 | 
				
			||||||
 | 
					  content: "\eb72";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-bantouming:before {
 | 
				
			||||||
 | 
					  content: "\e60b";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-chixushijian:before {
 | 
				
			||||||
 | 
					  content: "\e71a";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-duibi:before {
 | 
				
			||||||
 | 
					  content: "\e60c";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-huanyuan:before {
 | 
				
			||||||
 | 
					  content: "\e604";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-vr:before {
 | 
				
			||||||
 | 
					  content: "\e70a";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-chixushijian1:before {
 | 
				
			||||||
 | 
					  content: "\e51b";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/assets/style/font_oy0t1irww4k/iconfont.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										72
									
								
								src/assets/style/font_oy0t1irww4k/iconfont.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "id": "",
 | 
				
			||||||
 | 
					  "name": "",
 | 
				
			||||||
 | 
					  "font_family": "iconfont",
 | 
				
			||||||
 | 
					  "css_prefix_text": "icon-",
 | 
				
			||||||
 | 
					  "description": "",
 | 
				
			||||||
 | 
					  "glyphs": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "807275",
 | 
				
			||||||
 | 
					      "name": "全屏",
 | 
				
			||||||
 | 
					      "font_class": "quanping",
 | 
				
			||||||
 | 
					      "unicode": "e660",
 | 
				
			||||||
 | 
					      "unicode_decimal": 58976
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "1304879",
 | 
				
			||||||
 | 
					      "name": "帮助",
 | 
				
			||||||
 | 
					      "font_class": "bangzhu",
 | 
				
			||||||
 | 
					      "unicode": "e620",
 | 
				
			||||||
 | 
					      "unicode_decimal": 58912
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "5387852",
 | 
				
			||||||
 | 
					      "name": "帮助_o",
 | 
				
			||||||
 | 
					      "font_class": "bangzhu_o",
 | 
				
			||||||
 | 
					      "unicode": "eb72",
 | 
				
			||||||
 | 
					      "unicode_decimal": 60274
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "8883075",
 | 
				
			||||||
 | 
					      "name": "半透明(2)",
 | 
				
			||||||
 | 
					      "font_class": "bantouming",
 | 
				
			||||||
 | 
					      "unicode": "e60b",
 | 
				
			||||||
 | 
					      "unicode_decimal": 58891
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "11768105",
 | 
				
			||||||
 | 
					      "name": "持续时间",
 | 
				
			||||||
 | 
					      "font_class": "chixushijian",
 | 
				
			||||||
 | 
					      "unicode": "e71a",
 | 
				
			||||||
 | 
					      "unicode_decimal": 59162
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "15726777",
 | 
				
			||||||
 | 
					      "name": "对比",
 | 
				
			||||||
 | 
					      "font_class": "duibi",
 | 
				
			||||||
 | 
					      "unicode": "e60c",
 | 
				
			||||||
 | 
					      "unicode_decimal": 58892
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "16731254",
 | 
				
			||||||
 | 
					      "name": "还原",
 | 
				
			||||||
 | 
					      "font_class": "huanyuan",
 | 
				
			||||||
 | 
					      "unicode": "e604",
 | 
				
			||||||
 | 
					      "unicode_decimal": 58884
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "22273778",
 | 
				
			||||||
 | 
					      "name": "vr",
 | 
				
			||||||
 | 
					      "font_class": "vr",
 | 
				
			||||||
 | 
					      "unicode": "e70a",
 | 
				
			||||||
 | 
					      "unicode_decimal": 59146
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "icon_id": "42514699",
 | 
				
			||||||
 | 
					      "name": "持续时间",
 | 
				
			||||||
 | 
					      "font_class": "chixushijian1",
 | 
				
			||||||
 | 
					      "unicode": "e51b",
 | 
				
			||||||
 | 
					      "unicode_decimal": 58651
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/style/font_oy0t1irww4k/iconfont.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										124
									
								
								src/assets/style/global.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					* {
 | 
				
			||||||
 | 
					    outline: none !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html,
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    padding: 0px;
 | 
				
			||||||
 | 
					    margin: 0px;
 | 
				
			||||||
 | 
					    overflow: hidden !important;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    /* background: url("/resources/电脑端效果图.jpg") no-repeat center center fixed; */
 | 
				
			||||||
 | 
					    background-size: 100% 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					canvas {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    background: transparent;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:root {
 | 
				
			||||||
 | 
					    --cube-size: 30px;
 | 
				
			||||||
 | 
					    /* 定义方块大小变量 */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html {
 | 
				
			||||||
 | 
					    font-size: 16px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* {
 | 
				
			||||||
 | 
					    -webkit-tap-highlight-color: transparent;
 | 
				
			||||||
 | 
					    -webkit-touch-callout: none;
 | 
				
			||||||
 | 
					    /* 禁用长按菜单(如复制、保存图片等) */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#header {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100px;
 | 
				
			||||||
 | 
					    background-color: rgb(255, 255, 255);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#sidebar {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    width: 300px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* {
 | 
				
			||||||
 | 
					    -webkit-touch-callout: none;
 | 
				
			||||||
 | 
					    /*系统默认菜单被禁用*/
 | 
				
			||||||
 | 
					    -webkit-user-select: none;
 | 
				
			||||||
 | 
					    /*webkit浏览器*/
 | 
				
			||||||
 | 
					    -khtml-user-select: none;
 | 
				
			||||||
 | 
					    /*早期浏览器*/
 | 
				
			||||||
 | 
					    -moz-user-select: none;
 | 
				
			||||||
 | 
					    /*火狐*/
 | 
				
			||||||
 | 
					    -ms-user-select: none;
 | 
				
			||||||
 | 
					    /*IE10*/
 | 
				
			||||||
 | 
					    user-select: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* {
 | 
				
			||||||
 | 
					    -webkit-user-select: none;
 | 
				
			||||||
 | 
					    /* 禁止文字选中 */
 | 
				
			||||||
 | 
					    -webkit-touch-callout: none;
 | 
				
			||||||
 | 
					    /* 禁止长按弹出系统菜单 */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					input {
 | 
				
			||||||
 | 
					    -webkit-user-select: auto;
 | 
				
			||||||
 | 
					    /*webkit浏览器*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					textarea {
 | 
				
			||||||
 | 
					    -webkit-user-select: auto;
 | 
				
			||||||
 | 
					    /*webkit浏览器*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loadingfade {
 | 
				
			||||||
 | 
					    transition: all 0.8s ease-in;
 | 
				
			||||||
 | 
					    opacity: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.text {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    width: 200px;
 | 
				
			||||||
 | 
					    height: 60px;
 | 
				
			||||||
 | 
					    left: 50%;
 | 
				
			||||||
 | 
					    top: 60%;
 | 
				
			||||||
 | 
					    margin-left: -100px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    color: rgba(83, 83, 83, 0.582);
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    line-height: 60px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loaderbox {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    left: 50%;
 | 
				
			||||||
 | 
					    top: 50%;
 | 
				
			||||||
 | 
					    transform: translate(-50%, -50%);
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.button {
 | 
				
			||||||
 | 
					    pointer-events: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										90
									
								
								src/assets/style/loading.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					.loading-container {
 | 
				
			||||||
 | 
					    position: fixed;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					    width: 100vw;
 | 
				
			||||||
 | 
					    height: 100vh;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    justify-content: center;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    z-index: 9999;
 | 
				
			||||||
 | 
					    background: url('/loading/加载页背景图.jpg') no-repeat center center;
 | 
				
			||||||
 | 
					    background-size: 100% 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .loading-content {
 | 
				
			||||||
 | 
					    width: 230px;
 | 
				
			||||||
 | 
					    max-width: 600px;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    margin-top: -70px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					  .logo {
 | 
				
			||||||
 | 
					    width: 152px;
 | 
				
			||||||
 | 
					    font-size: 32px;
 | 
				
			||||||
 | 
					    font-weight: bold;
 | 
				
			||||||
 | 
					    margin-bottom: 50px;
 | 
				
			||||||
 | 
					    color: #333;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .logo img{
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .truck-container {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 40px;
 | 
				
			||||||
 | 
					    margin-top: -40px; /* 向上移动,与进度条重叠 */
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .progress-container {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 6px;
 | 
				
			||||||
 | 
					    background-color: rgba(255, 255, 255, 0.37);
 | 
				
			||||||
 | 
					    border-radius: 6px;
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    margin-bottom: 20px;
 | 
				
			||||||
 | 
					    padding: 2px;
 | 
				
			||||||
 | 
					    box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    align-items: center; /* 保持垂直居中 */
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .progress-bar {
 | 
				
			||||||
 | 
					    height: 4px; /* 保持这个高度 */
 | 
				
			||||||
 | 
					    background-color: #1087d6;
 | 
				
			||||||
 | 
					    transition: width 0.3s;
 | 
				
			||||||
 | 
					    border-radius: 3px;
 | 
				
			||||||
 | 
					    /* 移除这两行,它们导致了中间扩散效果 */
 | 
				
			||||||
 | 
					    /* margin: 0 auto; */
 | 
				
			||||||
 | 
					    /* align-self: center; */
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /* 添加这行确保从左边开始 */
 | 
				
			||||||
 | 
					    margin-left: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .truck {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    transform: translateX(-90%); /* 改为-90%,让小车更靠左一些 */
 | 
				
			||||||
 | 
					    transition: left 0.3s;
 | 
				
			||||||
 | 
					    z-index: 10;
 | 
				
			||||||
 | 
					    bottom: 24px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .truck img {
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    width: 80px;
 | 
				
			||||||
 | 
					    height: auto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .loading-text {
 | 
				
			||||||
 | 
					    font-size: 14px;
 | 
				
			||||||
 | 
					    color: #333;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
							
								
								
									
										286
									
								
								src/assets/style/media.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,286 @@
 | 
				
			|||||||
 | 
					@charset "UTF-8";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 移动设备 */
 | 
				
			||||||
 | 
					@media screen and (max-width: 350px) {
 | 
				
			||||||
 | 
					    .title {
 | 
				
			||||||
 | 
					        font-size: 16px !important;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 移动设备 */
 | 
				
			||||||
 | 
					@media screen and (max-width: 640px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .loaderbox img {
 | 
				
			||||||
 | 
					        width: 200px !important;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .jiudu {
 | 
				
			||||||
 | 
					        min-width: 70px;
 | 
				
			||||||
 | 
					        height: 36px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 横屏*/
 | 
				
			||||||
 | 
					    @media screen and (max-height: 766px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ui-bottom {
 | 
				
			||||||
 | 
					            bottom: 20px;
 | 
				
			||||||
 | 
					            right: 10px;
 | 
				
			||||||
 | 
					            /* 通过偏移实现居中 */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item {
 | 
				
			||||||
 | 
					            width: 5rem;
 | 
				
			||||||
 | 
					            height: 5rem;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item .Bar-Line {
 | 
				
			||||||
 | 
					            width: calc(5rem + 6px);
 | 
				
			||||||
 | 
					            height: calc(5rem + 6px);
 | 
				
			||||||
 | 
					            box-shadow: 0 0 0 1px #fff;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-body {
 | 
				
			||||||
 | 
					            padding: 5px 5px
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .TopInfo-content .title {
 | 
				
			||||||
 | 
					            font-size: 24px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 竖屏*/
 | 
				
			||||||
 | 
					    @media screen and (min-height: 768px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ui-bottom {
 | 
				
			||||||
 | 
					            bottom: 20px;
 | 
				
			||||||
 | 
					            right: 10px;
 | 
				
			||||||
 | 
					            /* 通过偏移实现居中 */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item {
 | 
				
			||||||
 | 
					            width: 5rem;
 | 
				
			||||||
 | 
					            height: 5rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item .Bar-Line {
 | 
				
			||||||
 | 
					            width: calc(5rem + 6px);
 | 
				
			||||||
 | 
					            height: calc(5rem + 6px);
 | 
				
			||||||
 | 
					            box-shadow: 0 0 0 1px #fff;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-body {
 | 
				
			||||||
 | 
					            padding: 5px 5px
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .TopInfo-content .title {
 | 
				
			||||||
 | 
					            font-size: 20px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ColorBar1-container {
 | 
				
			||||||
 | 
					            top: 20px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* .ColorBar-container {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            left: 0;
 | 
				
			||||||
 | 
					            top: 20px;
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					            flex-direction: column;
 | 
				
			||||||
 | 
					            justify-content: space-evenly;
 | 
				
			||||||
 | 
					        } */
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .return {
 | 
				
			||||||
 | 
					        width: 40px;
 | 
				
			||||||
 | 
					        height: 40px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 移动设备 */
 | 
				
			||||||
 | 
					@media screen and (max-width: 767px) and (min-width: 640px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 横屏*/
 | 
				
			||||||
 | 
					    @media screen and (max-height: 400px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ui-bottom {
 | 
				
			||||||
 | 
					            bottom: 20px;
 | 
				
			||||||
 | 
					            right: 10px;
 | 
				
			||||||
 | 
					            /* 通过偏移实现居中 */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item {
 | 
				
			||||||
 | 
					            width: 7rem;
 | 
				
			||||||
 | 
					            height: 7rem;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item .Bar-Line {
 | 
				
			||||||
 | 
					            width: calc(7rem + 6px);
 | 
				
			||||||
 | 
					            height: calc(7rem + 6px);
 | 
				
			||||||
 | 
					            box-shadow: 0 0 0 1px #fff;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-body {
 | 
				
			||||||
 | 
					            padding: 30px 10px !important;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-bottom {
 | 
				
			||||||
 | 
					            height: 30px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 横屏*/
 | 
				
			||||||
 | 
					    @media screen and (max-height: 766px) and (min-height: 400px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ui-bottom {
 | 
				
			||||||
 | 
					            bottom: 20px;
 | 
				
			||||||
 | 
					            right: 10px;
 | 
				
			||||||
 | 
					            /* 通过偏移实现居中 */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item {
 | 
				
			||||||
 | 
					            width: 9rem;
 | 
				
			||||||
 | 
					            height: 9rem;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item .Bar-Line {
 | 
				
			||||||
 | 
					            width: calc(9rem + 6px);
 | 
				
			||||||
 | 
					            height: calc(9rem + 6px);
 | 
				
			||||||
 | 
					            box-shadow: 0 0 0 1px #fff;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-body {
 | 
				
			||||||
 | 
					            padding: 5px 5px
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 竖屏*/
 | 
				
			||||||
 | 
					    @media screen and (min-height: 768px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ui-bottom {
 | 
				
			||||||
 | 
					            bottom: 20px;
 | 
				
			||||||
 | 
					            right: 10px;
 | 
				
			||||||
 | 
					            /* 通过偏移实现居中 */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item {
 | 
				
			||||||
 | 
					            width: 10rem;
 | 
				
			||||||
 | 
					            height: 10rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item .Bar-Line {
 | 
				
			||||||
 | 
					            width: calc(10rem + 6px);
 | 
				
			||||||
 | 
					            height: calc(10rem + 6px);
 | 
				
			||||||
 | 
					            box-shadow: 0 0 0 1px #fff;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-body {
 | 
				
			||||||
 | 
					            padding: 5px 5px
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .TopInfo-content .title {
 | 
				
			||||||
 | 
					            font-size: 24px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .return {
 | 
				
			||||||
 | 
					        width: 50px;
 | 
				
			||||||
 | 
					        height: 50px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 电脑设备 */
 | 
				
			||||||
 | 
					@media screen and (min-width: 768px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 横屏*/
 | 
				
			||||||
 | 
					    @media screen and (max-height: 400px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ui-bottom {
 | 
				
			||||||
 | 
					            bottom: 20px;
 | 
				
			||||||
 | 
					            right: 10px;
 | 
				
			||||||
 | 
					            /* 通过偏移实现居中 */
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item {
 | 
				
			||||||
 | 
					            width: 7rem;
 | 
				
			||||||
 | 
					            height: 7rem;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item .Bar-Line {
 | 
				
			||||||
 | 
					            width: calc(7rem + 6px);
 | 
				
			||||||
 | 
					            height: calc(7rem + 6px);
 | 
				
			||||||
 | 
					            box-shadow: 0 0 0 1px #fff;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-body {
 | 
				
			||||||
 | 
					            padding: 30px 10px !important;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 横屏*/
 | 
				
			||||||
 | 
					    @media screen and (min-height: 400px) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ui-bottom {
 | 
				
			||||||
 | 
					            right: 20px;
 | 
				
			||||||
 | 
					            bottom: 0px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item {
 | 
				
			||||||
 | 
					            width: 12.5rem;
 | 
				
			||||||
 | 
					            height: 12.5rem;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .dialog-item .Bar-Line {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            width: calc(12.5rem + 6px);
 | 
				
			||||||
 | 
					            height: calc(12.5rem + 6px);
 | 
				
			||||||
 | 
					            box-shadow: 0 0 0 1px #fff;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .ColorBar1-container {
 | 
				
			||||||
 | 
					            top: 40px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .jiudu {
 | 
				
			||||||
 | 
					            min-width: 5rem;
 | 
				
			||||||
 | 
					            height: 2rem;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .return {
 | 
				
			||||||
 | 
					        width: 50px;
 | 
				
			||||||
 | 
					        height: 50px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/assets/style/normal.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					html,
 | 
				
			||||||
 | 
					body {
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					    margin  : 0;
 | 
				
			||||||
 | 
					    padding : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/components.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					// @ts-nocheck
 | 
				
			||||||
 | 
					// Generated by unplugin-vue-components
 | 
				
			||||||
 | 
					// Read more: https://github.com/vuejs/core/pull/3399
 | 
				
			||||||
 | 
					// biome-ignore lint: disable
 | 
				
			||||||
 | 
					export {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* prettier-ignore */
 | 
				
			||||||
 | 
					declare module 'vue' {
 | 
				
			||||||
 | 
					  export interface GlobalComponents {
 | 
				
			||||||
 | 
					    AnimationCard: typeof import('./components/AnimationCard.vue')['default']
 | 
				
			||||||
 | 
					    AnimationControls: typeof import('./components/AnimationControls.vue')['default']
 | 
				
			||||||
 | 
					    AnimationLibrary: typeof import('./components/AnimationLibrary.vue')['default']
 | 
				
			||||||
 | 
					    AnimationManager: typeof import('./components/AnimationManager.vue')['default']
 | 
				
			||||||
 | 
					    ButtonToggle: typeof import('./components/ButtonToggle.vue')['default']
 | 
				
			||||||
 | 
					    ColorPicker: typeof import('./components/ColorPicker.vue')['default']
 | 
				
			||||||
 | 
					    CustomizationIndex: typeof import('./components/CustomizationIndex.vue')['default']
 | 
				
			||||||
 | 
					    ItemSelector: typeof import('./components/ItemSelector.vue')['default']
 | 
				
			||||||
 | 
					    MaterialColorPicker: typeof import('./components/MaterialColorPicker.vue')['default']
 | 
				
			||||||
 | 
					    PresetPanel: typeof import('./components/PresetPanel.vue')['default']
 | 
				
			||||||
 | 
					    RouterLink: typeof import('vue-router')['RouterLink']
 | 
				
			||||||
 | 
					    RouterView: typeof import('vue-router')['RouterView']
 | 
				
			||||||
 | 
					    Simple3DView: typeof import('./components/Simple3DView.vue')['default']
 | 
				
			||||||
 | 
					    SliderControl: typeof import('./components/SliderControl.vue')['default']
 | 
				
			||||||
 | 
					    TabContent: typeof import('./components/TabContent.vue')['default']
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										118
									
								
								src/components/CUSTOMIZATION_README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					# 定制化功能使用说明
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 概述
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					这是一个基于Babylon.js的定制化系统,允许用户在3D模型上添加图形和颜色层。系统采用了模块化设计,遵循项目的编码规范。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 架构说明
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. AppCustomization (核心逻辑)
 | 
				
			||||||
 | 
					- **位置**: `src/script/public/AppCustomization.ts`
 | 
				
			||||||
 | 
					- **功能**: 
 | 
				
			||||||
 | 
					  - 管理动态纹理的创建和更新
 | 
				
			||||||
 | 
					  - 监听Pinia store的变化并更新纹理
 | 
				
			||||||
 | 
					  - 处理图形的绘制和矩形填充
 | 
				
			||||||
 | 
					  - 资源加载管理
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. CustomizationStore (状态管理)
 | 
				
			||||||
 | 
					- **位置**: `src/stores/customization.ts`
 | 
				
			||||||
 | 
					- **功能**:
 | 
				
			||||||
 | 
					  - 管理层的状态(最多5层)
 | 
				
			||||||
 | 
					  - 提供层的增删改查功能
 | 
				
			||||||
 | 
					  - 触发纹理更新标记
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. CustomizationIndex (UI组件)
 | 
				
			||||||
 | 
					- **位置**: `src/components/CustomizationIndex.vue`
 | 
				
			||||||
 | 
					- **功能**:
 | 
				
			||||||
 | 
					  - 显示内容选择界面(颜色和图形)
 | 
				
			||||||
 | 
					  - 显示层控制面板(位置、尺寸、旋转等参数)
 | 
				
			||||||
 | 
					  - 显示层列表和操作按钮
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 使用方法
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. 基本使用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					系统已经集成到 `MainApp.vue` 中,UI组件会自动显示。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. 初始化
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					在 `View.vue` 中,store会在组件挂载时自动初始化:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  customizationStore.initializeLayers();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. 添加图形资源
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					在 `AppCustomization.ts` 中的 `loadGraphicAssets` 方法中配置图形资源:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const graphicNames = [
 | 
				
			||||||
 | 
					  'Babylon Logo',
 | 
				
			||||||
 | 
					  'Flame Skull',
 | 
				
			||||||
 | 
					  // ... 添加更多图形名称
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 4. 应用贴花材质到模型
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					在模型加载完成后,可以通过 `MainEditor` 获取 `AppCustomization` 实例:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const customization = mainEditor.mainApp.appCustomization;
 | 
				
			||||||
 | 
					await customization.createDecalMaterial(baseMaterial, shaderJsonPath);
 | 
				
			||||||
 | 
					customization.applyDecalMaterialToMesh(mesh);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 工作原理
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **纹理更新流程**:
 | 
				
			||||||
 | 
					   - 用户在UI中修改层参数
 | 
				
			||||||
 | 
					   - Store中的 `textureUpdateTrigger` 增加
 | 
				
			||||||
 | 
					   - `AppCustomization` 监听到变化
 | 
				
			||||||
 | 
					   - 调用 `updateImprintTexture()` 重新绘制纹理
 | 
				
			||||||
 | 
					   - 纹理自动更新到3D场景
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. **层管理**:
 | 
				
			||||||
 | 
					   - 最多支持5个层
 | 
				
			||||||
 | 
					   - 每个层可以是颜色(矩形)或图形
 | 
				
			||||||
 | 
					   - 层可以上下移动调整顺序
 | 
				
			||||||
 | 
					   - 每个层都有独立的参数控制
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 扩展说明
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 添加新的图形资源
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. 将图形文件放在服务器资源目录
 | 
				
			||||||
 | 
					2. 在 `AppCustomization.loadGraphicAssets()` 中添加资源名称
 | 
				
			||||||
 | 
					3. 资源会自动加载并添加到store中
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 修改层数量
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					在 `stores/customization.ts` 中修改 `maxLayers` 的值:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```typescript
 | 
				
			||||||
 | 
					const maxLayers = ref(5) // 改为你想要的数量
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 自定义颜色列表
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					在 `stores/customization.ts` 中修改 `AVAILABLE_COLORS` 数组。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 注意事项
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. 纹理更新使用了防抖机制(100ms),避免频繁更新
 | 
				
			||||||
 | 
					2. 确保图形资源的路径配置正确
 | 
				
			||||||
 | 
					3. 贴花材质需要对应的着色器JSON文件
 | 
				
			||||||
 | 
					4. 纹理尺寸固定为 683x2048,如需修改请修改 `AppCustomization` 中的常量
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 技术特点
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- ✅ 模块化设计,易于扩展
 | 
				
			||||||
 | 
					- ✅ 使用Pinia进行状态管理
 | 
				
			||||||
 | 
					- ✅ 响应式更新,自动同步UI和3D场景
 | 
				
			||||||
 | 
					- ✅ 防抖优化,提升性能
 | 
				
			||||||
 | 
					- ✅ 符合项目编码规范
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										481
									
								
								src/components/CustomizationIndex.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,481 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="customization-ui">
 | 
				
			||||||
 | 
					    <!-- 内容选择区域 -->
 | 
				
			||||||
 | 
					    <div v-if="displayMode === 'content'" class="content-selection">
 | 
				
			||||||
 | 
					      <div class="content-grid">
 | 
				
			||||||
 | 
					        <!-- 颜色按钮 -->
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          v-for="color in AVAILABLE_COLORS"
 | 
				
			||||||
 | 
					          :key="color"
 | 
				
			||||||
 | 
					          class="color-button"
 | 
				
			||||||
 | 
					          :style="{ backgroundColor: getColorValue(color) }"
 | 
				
			||||||
 | 
					          @click="selectColor(color)"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        <!-- 图形按钮 -->
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          v-for="graphic in graphics"
 | 
				
			||||||
 | 
					          :key="graphic.name"
 | 
				
			||||||
 | 
					          class="graphic-button"
 | 
				
			||||||
 | 
					          @click="selectGraphic(graphic)"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <img :src="graphic.url" :alt="graphic.name" />
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 层控制区域 -->
 | 
				
			||||||
 | 
					    <div v-if="displayMode === 'controls' && currentLayerInfo" class="layer-controls">
 | 
				
			||||||
 | 
					      <div class="control-panel">
 | 
				
			||||||
 | 
					        <!-- 水平位置 -->
 | 
				
			||||||
 | 
					        <div class="control-item">
 | 
				
			||||||
 | 
					          <label>水平位置</label>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type="range"
 | 
				
			||||||
 | 
					            :min="currentLayerInfo.rectangle ? -1 : -2"
 | 
				
			||||||
 | 
					            :max="currentLayerInfo.rectangle ? 1 : 2"
 | 
				
			||||||
 | 
					            :step="0.01"
 | 
				
			||||||
 | 
					            :value="currentLayerInfo.horizontal"
 | 
				
			||||||
 | 
					            @input="updateParameter('horizontal', parseFloat(($event.target as HTMLInputElement).value))"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <span>{{ currentLayerInfo.horizontal.toFixed(2) }}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 垂直位置 -->
 | 
				
			||||||
 | 
					        <div class="control-item">
 | 
				
			||||||
 | 
					          <label>垂直位置</label>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type="range"
 | 
				
			||||||
 | 
					            :min="currentLayerInfo.rectangle ? -1 : -2"
 | 
				
			||||||
 | 
					            :max="currentLayerInfo.rectangle ? 1 : 2"
 | 
				
			||||||
 | 
					            :step="0.01"
 | 
				
			||||||
 | 
					            :value="currentLayerInfo.vertical"
 | 
				
			||||||
 | 
					            @input="updateParameter('vertical', parseFloat(($event.target as HTMLInputElement).value))"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <span>{{ currentLayerInfo.vertical.toFixed(2) }}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 尺寸(仅图形) -->
 | 
				
			||||||
 | 
					        <div v-if="!currentLayerInfo.rectangle" class="control-item">
 | 
				
			||||||
 | 
					          <label>尺寸</label>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type="range"
 | 
				
			||||||
 | 
					            min="0"
 | 
				
			||||||
 | 
					            max="2048"
 | 
				
			||||||
 | 
					            step="1"
 | 
				
			||||||
 | 
					            :value="currentLayerInfo.size"
 | 
				
			||||||
 | 
					            @input="updateParameter('size', parseFloat(($event.target as HTMLInputElement).value))"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <span>{{ currentLayerInfo.size }}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 宽度(仅矩形) -->
 | 
				
			||||||
 | 
					        <div v-if="currentLayerInfo.rectangle" class="control-item">
 | 
				
			||||||
 | 
					          <label>宽度</label>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type="range"
 | 
				
			||||||
 | 
					            min="0"
 | 
				
			||||||
 | 
					            max="2"
 | 
				
			||||||
 | 
					            step="0.01"
 | 
				
			||||||
 | 
					            :value="currentLayerInfo.width"
 | 
				
			||||||
 | 
					            @input="updateParameter('width', parseFloat(($event.target as HTMLInputElement).value))"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <span>{{ currentLayerInfo.width.toFixed(2) }}</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 高度/旋转 -->
 | 
				
			||||||
 | 
					        <div class="control-item">
 | 
				
			||||||
 | 
					          <label>{{ currentLayerInfo.rectangle ? '高度' : '旋转' }}</label>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type="range"
 | 
				
			||||||
 | 
					            :min="currentLayerInfo.rectangle ? 0 : -Math.PI"
 | 
				
			||||||
 | 
					            :max="currentLayerInfo.rectangle ? 2 : Math.PI"
 | 
				
			||||||
 | 
					            :step="currentLayerInfo.rectangle ? 0.01 : 0.01"
 | 
				
			||||||
 | 
					            :value="currentLayerInfo.rectangle ? currentLayerInfo.height : currentLayerInfo.rotation"
 | 
				
			||||||
 | 
					            @input="updateParameter(
 | 
				
			||||||
 | 
					              currentLayerInfo.rectangle ? 'height' : 'rotation',
 | 
				
			||||||
 | 
					              parseFloat(($event.target as HTMLInputElement).value)
 | 
				
			||||||
 | 
					            )"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <span>
 | 
				
			||||||
 | 
					            {{ currentLayerInfo.rectangle
 | 
				
			||||||
 | 
					              ? currentLayerInfo.height.toFixed(2)
 | 
				
			||||||
 | 
					              : (currentLayerInfo.rotation * 180 / Math.PI).toFixed(1) + '°'
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 旋转(矩形) -->
 | 
				
			||||||
 | 
					        <div v-if="currentLayerInfo.rectangle" class="control-item">
 | 
				
			||||||
 | 
					          <label>旋转</label>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type="range"
 | 
				
			||||||
 | 
					            :min="-Math.PI"
 | 
				
			||||||
 | 
					            :max="Math.PI"
 | 
				
			||||||
 | 
					            step="0.01"
 | 
				
			||||||
 | 
					            :value="currentLayerInfo.rotation"
 | 
				
			||||||
 | 
					            @input="updateParameter('rotation', parseFloat(($event.target as HTMLInputElement).value))"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					          <span>{{ (currentLayerInfo.rotation * 180 / Math.PI).toFixed(1) }}°</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 层列表 -->
 | 
				
			||||||
 | 
					    <div class="layers-list">
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        v-for="(layer, index) in layers"
 | 
				
			||||||
 | 
					        :key="index"
 | 
				
			||||||
 | 
					        class="layer-item"
 | 
				
			||||||
 | 
					        :class="{ active: currentLayer === index }"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <!-- 层选择按钮 -->
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          class="layer-select-btn"
 | 
				
			||||||
 | 
					          @click="selectLayer(index)"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          {{ layer.name || `层 ${index + 1}` }}
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 层操作按钮 -->
 | 
				
			||||||
 | 
					        <div v-if="layer.visible" class="layer-actions">
 | 
				
			||||||
 | 
					          <button
 | 
				
			||||||
 | 
					            class="action-btn up-btn"
 | 
				
			||||||
 | 
					            :disabled="index === 0"
 | 
				
			||||||
 | 
					            @click="moveLayer(index, 'up')"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            ↑
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					          <button
 | 
				
			||||||
 | 
					            class="action-btn down-btn"
 | 
				
			||||||
 | 
					            :disabled="index === maxLayers - 1"
 | 
				
			||||||
 | 
					            @click="moveLayer(index, 'down')"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            ↓
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					          <button
 | 
				
			||||||
 | 
					            class="action-btn delete-btn"
 | 
				
			||||||
 | 
					            @click="removeLayer(index)"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            ×
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 添加内容按钮 -->
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          v-else
 | 
				
			||||||
 | 
					          class="add-content-btn"
 | 
				
			||||||
 | 
					          @click="addContentToLayer(index)"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          +
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import { computed, watch } from 'vue'
 | 
				
			||||||
 | 
					import { useCustomizationStore, AVAILABLE_COLORS } from '@/stores/customization'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const store = useCustomizationStore()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 计算属性
 | 
				
			||||||
 | 
					const displayMode = computed(() => store.displayMode)
 | 
				
			||||||
 | 
					const currentLayer = computed(() => store.currentLayer)
 | 
				
			||||||
 | 
					const currentLayerInfo = computed(() => store.currentLayerInfo)
 | 
				
			||||||
 | 
					const layers = computed(() => store.layers)
 | 
				
			||||||
 | 
					const graphics = computed(() => store.graphics)
 | 
				
			||||||
 | 
					const maxLayers = computed(() => store.maxLayers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 监听纹理更新触发
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					  () => store.textureUpdateTrigger,
 | 
				
			||||||
 | 
					  () => {
 | 
				
			||||||
 | 
					    // 纹理更新由AppCustomization类监听处理
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 选择颜色
 | 
				
			||||||
 | 
					const selectColor = (colorName: string) => {
 | 
				
			||||||
 | 
					  if (currentLayer.value === undefined) {
 | 
				
			||||||
 | 
					    // 如果没有选中层,选择第一个空层
 | 
				
			||||||
 | 
					    const emptyLayerIndex = store.layers.findIndex(layer => !layer.visible)
 | 
				
			||||||
 | 
					    if (emptyLayerIndex !== -1) {
 | 
				
			||||||
 | 
					      store.selectLayer(emptyLayerIndex)
 | 
				
			||||||
 | 
					      store.showContentSelection()
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.warn('没有可用的层')
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (currentLayer.value !== undefined) {
 | 
				
			||||||
 | 
					    const colorValue = getColorValue(colorName)
 | 
				
			||||||
 | 
					    store.addContentToLayer(currentLayer.value, {
 | 
				
			||||||
 | 
					      name: colorName,
 | 
				
			||||||
 | 
					      type: 'color',
 | 
				
			||||||
 | 
					      fillStyle: colorValue
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    store.showLayerControls()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 选择图形
 | 
				
			||||||
 | 
					const selectGraphic = (graphic: any) => {
 | 
				
			||||||
 | 
					  if (currentLayer.value === undefined) {
 | 
				
			||||||
 | 
					    // 如果没有选中层,选择第一个空层
 | 
				
			||||||
 | 
					    const emptyLayerIndex = store.layers.findIndex(layer => !layer.visible)
 | 
				
			||||||
 | 
					    if (emptyLayerIndex !== -1) {
 | 
				
			||||||
 | 
					      store.selectLayer(emptyLayerIndex)
 | 
				
			||||||
 | 
					      store.showContentSelection()
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.warn('没有可用的层')
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (currentLayer.value !== undefined && graphic.image) {
 | 
				
			||||||
 | 
					    store.addContentToLayer(currentLayer.value, {
 | 
				
			||||||
 | 
					      name: graphic.name,
 | 
				
			||||||
 | 
					      type: 'graphic',
 | 
				
			||||||
 | 
					      graphic: graphic.image
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    store.showLayerControls()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 选择层
 | 
				
			||||||
 | 
					const selectLayer = (index: number) => {
 | 
				
			||||||
 | 
					  store.selectLayer(index)
 | 
				
			||||||
 | 
					  if (store.layers[index].visible) {
 | 
				
			||||||
 | 
					    store.showLayerControls()
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    store.showContentSelection()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 添加内容到层
 | 
				
			||||||
 | 
					const addContentToLayer = (index: number) => {
 | 
				
			||||||
 | 
					  store.selectLayer(index)
 | 
				
			||||||
 | 
					  store.showContentSelection()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 更新层参数
 | 
				
			||||||
 | 
					const updateParameter = (parameter: string, value: number) => {
 | 
				
			||||||
 | 
					  if (currentLayer.value !== undefined) {
 | 
				
			||||||
 | 
					    store.updateLayerParameter(
 | 
				
			||||||
 | 
					      currentLayer.value,
 | 
				
			||||||
 | 
					      parameter as any,
 | 
				
			||||||
 | 
					      value
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 移动层
 | 
				
			||||||
 | 
					const moveLayer = (index: number, direction: 'up' | 'down') => {
 | 
				
			||||||
 | 
					  store.moveLayer(index, direction)
 | 
				
			||||||
 | 
					  // 移动后保持选中状态,但不改变显示模式
 | 
				
			||||||
 | 
					  if (store.currentLayer === index || store.currentLayer === (direction === 'up' ? index - 1 : index + 1)) {
 | 
				
			||||||
 | 
					    // 如果移动的是当前选中的层,保持选中状态
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 删除层内容
 | 
				
			||||||
 | 
					const removeLayer = (index: number) => {
 | 
				
			||||||
 | 
					  store.removeLayerContent(index)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取颜色值
 | 
				
			||||||
 | 
					const getColorValue = (colorName: string): string => {
 | 
				
			||||||
 | 
					  // 简单的颜色映射,可以根据需要扩展
 | 
				
			||||||
 | 
					  const colorMap: Record<string, string> = {
 | 
				
			||||||
 | 
					    'Azure': '#F0FFFF',
 | 
				
			||||||
 | 
					    'Black': '#000000',
 | 
				
			||||||
 | 
					    'DarkGray': '#A9A9A9',
 | 
				
			||||||
 | 
					    'DimGray': '#696969',
 | 
				
			||||||
 | 
					    'Red': '#FF0000',
 | 
				
			||||||
 | 
					    'FireBrick': '#B22222',
 | 
				
			||||||
 | 
					    'DeepPink': '#FF1493',
 | 
				
			||||||
 | 
					    'OrangeRed': '#FF4500',
 | 
				
			||||||
 | 
					    'Orange': '#FFA500',
 | 
				
			||||||
 | 
					    'Gold': '#FFD700',
 | 
				
			||||||
 | 
					    'Yellow': '#FFFF00',
 | 
				
			||||||
 | 
					    'RoyalBlue': '#4169E1',
 | 
				
			||||||
 | 
					    'CornflowerBlue': '#6495ED',
 | 
				
			||||||
 | 
					    'DeepSkyBlue': '#00BFFF',
 | 
				
			||||||
 | 
					    'DarkGreen': '#006400',
 | 
				
			||||||
 | 
					    'ForestGreen': '#228B22',
 | 
				
			||||||
 | 
					    'GreenYellow': '#ADFF2F',
 | 
				
			||||||
 | 
					    'RebeccaPurple': '#663399',
 | 
				
			||||||
 | 
					    'DarkViolet': '#9400D3',
 | 
				
			||||||
 | 
					    'MediumPurple': '#9370DB'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return colorMap[colorName] || '#000000'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.customization-ui {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  left: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  pointer-events: none;
 | 
				
			||||||
 | 
					  z-index: 100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.content-selection,
 | 
				
			||||||
 | 
					.layer-controls {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  right: 20px;
 | 
				
			||||||
 | 
					  top: 50%;
 | 
				
			||||||
 | 
					  transform: translateY(-50%);
 | 
				
			||||||
 | 
					  background: rgba(30, 30, 35, 0.9);
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  pointer-events: all;
 | 
				
			||||||
 | 
					  max-width: 400px;
 | 
				
			||||||
 | 
					  max-height: 80vh;
 | 
				
			||||||
 | 
					  overflow-y: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.content-grid {
 | 
				
			||||||
 | 
					  display: grid;
 | 
				
			||||||
 | 
					  grid-template-columns: repeat(4, 1fr);
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.color-button,
 | 
				
			||||||
 | 
					.graphic-button {
 | 
				
			||||||
 | 
					  width: 60px;
 | 
				
			||||||
 | 
					  height: 60px;
 | 
				
			||||||
 | 
					  border: 2px solid #fff;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  background: transparent;
 | 
				
			||||||
 | 
					  padding: 4px;
 | 
				
			||||||
 | 
					  transition: all 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.color-button:hover,
 | 
				
			||||||
 | 
					.graphic-button:hover {
 | 
				
			||||||
 | 
					  border-color: #e42d2d;
 | 
				
			||||||
 | 
					  transform: scale(1.1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.graphic-button img {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  object-fit: contain;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.control-panel {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  gap: 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.control-item {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  gap: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.control-item label {
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  margin-bottom: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.control-item input[type="range"] {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.control-item span {
 | 
				
			||||||
 | 
					  color: #ccc;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  text-align: right;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.layers-list {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  left: 20px;
 | 
				
			||||||
 | 
					  top: 50%;
 | 
				
			||||||
 | 
					  transform: translateY(-50%);
 | 
				
			||||||
 | 
					  background: rgba(30, 30, 35, 0.9);
 | 
				
			||||||
 | 
					  padding: 15px;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  pointer-events: all;
 | 
				
			||||||
 | 
					  min-width: 200px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.layer-item {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  gap: 8px;
 | 
				
			||||||
 | 
					  margin-bottom: 8px;
 | 
				
			||||||
 | 
					  padding: 8px;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  background: rgba(56, 26, 26, 1);
 | 
				
			||||||
 | 
					  transition: background 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.layer-item.active {
 | 
				
			||||||
 | 
					  background: rgba(228, 45, 45, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.layer-select-btn {
 | 
				
			||||||
 | 
					  flex: 1;
 | 
				
			||||||
 | 
					  padding: 8px;
 | 
				
			||||||
 | 
					  background: transparent;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  text-align: left;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.layer-actions {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  gap: 4px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.action-btn,
 | 
				
			||||||
 | 
					.add-content-btn {
 | 
				
			||||||
 | 
					  width: 28px;
 | 
				
			||||||
 | 
					  height: 28px;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  background: rgba(255, 255, 255, 0.2);
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  transition: all 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.action-btn:hover,
 | 
				
			||||||
 | 
					.add-content-btn:hover {
 | 
				
			||||||
 | 
					  background: rgba(255, 255, 255, 0.4);
 | 
				
			||||||
 | 
					  transform: scale(1.1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.action-btn:disabled {
 | 
				
			||||||
 | 
					  opacity: 0.3;
 | 
				
			||||||
 | 
					  cursor: not-allowed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.delete-btn {
 | 
				
			||||||
 | 
					  background: rgba(255, 0, 0, 0.3);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.delete-btn:hover {
 | 
				
			||||||
 | 
					  background: rgba(255, 0, 0, 0.5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										76
									
								
								src/components/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										8
									
								
								src/env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					/// <reference types="vite/client" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare module '*.vue' {
 | 
				
			||||||
 | 
					  import type { DefineComponent } from 'vue'
 | 
				
			||||||
 | 
					  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
 | 
				
			||||||
 | 
					  const component: DefineComponent<{}, {}, any>
 | 
				
			||||||
 | 
					  export default component
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										76
									
								
								src/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										765
									
								
								src/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,765 @@
 | 
				
			|||||||
 | 
					var createScene = async function () {
 | 
				
			||||||
 | 
					    // This creates a basic Babylon Scene object (non-mesh)
 | 
				
			||||||
 | 
					    var scene = new BABYLON.Scene(engine);
 | 
				
			||||||
 | 
					    const server = "https://patrickryanms.github.io/BabylonJStextures/Demos/productCustomizer/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const screen = {};
 | 
				
			||||||
 | 
					    // determine the size of the viewport minus the gutter based on the current window width
 | 
				
			||||||
 | 
					    function setViewportSize() {
 | 
				
			||||||
 | 
					        screen.width = engine.getRenderWidth();
 | 
				
			||||||
 | 
					        screen.height = engine.getRenderHeight();
 | 
				
			||||||
 | 
					        screen.ratio = screen.width / screen.height;
 | 
				
			||||||
 | 
					        camera.productWidth = 0.6;
 | 
				
			||||||
 | 
					        camera.product.viewport = new BABYLON.Viewport(1.0 - camera.productWidth, 0.0, camera.productWidth, 1.0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function updateViewportSize() {
 | 
				
			||||||
 | 
					        screen.width = engine.getRenderWidth();
 | 
				
			||||||
 | 
					        screen.height = engine.getRenderHeight();
 | 
				
			||||||
 | 
					        screen.ratio = screen.width / screen.height;
 | 
				
			||||||
 | 
					        // resize content buttons based on screen size
 | 
				
			||||||
 | 
					        for (let control of gui.contentButtons) {
 | 
				
			||||||
 | 
					            if (screen.ratio < 0.6) {
 | 
				
			||||||
 | 
					                let calculatedWidth = ((screen.width * gui.layoutGrid.getColumnDefinition(0)._value) / gui.contentButtonsGrid.columnCount) * 0.7;
 | 
				
			||||||
 | 
					                control.width = Math.floor(calculatedWidth).toString() + "px";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                let calculatedHeight = ((screen.height * gui.layoutGrid.getRowDefinition(1)._value)/ gui.contentButtonsGrid.rowCount) * 0.7;
 | 
				
			||||||
 | 
					                control.height = Math.floor(calculatedHeight).toString() + "px";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // resize layer buttons based on screen size
 | 
				
			||||||
 | 
					        if (gui.buttons !== undefined) {
 | 
				
			||||||
 | 
					            for (let control of gui.buttons) {
 | 
				
			||||||
 | 
					                if (screen.ratio < 0.6 && control.name.split("_")[2] !== "selectButton") {
 | 
				
			||||||
 | 
					                    if (control._children[0] !== undefined) {
 | 
				
			||||||
 | 
					                        control._children[0].height = "95%";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    let calculatedWidth = ((screen.width * gui.layoutGrid.getColumnDefinition(0)._value) * gui.layerUiGrid[0].getColumnDefinition(1)._value);
 | 
				
			||||||
 | 
					                    control.width = Math.floor(calculatedWidth).toString() + "px";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    if (control._children[0] !== undefined) {
 | 
				
			||||||
 | 
					                        control._children[0].height = "60%";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    let calculatedHeight = ((screen.height * gui.layoutGrid.getRowDefinition(0)._value) * gui.layersGrid.getRowDefinition(1)._value);
 | 
				
			||||||
 | 
					                    control.height = Math.floor(calculatedHeight).toString() + "px";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // resize layer grid width based on screen size for button position
 | 
				
			||||||
 | 
					        if (gui.layerUiGrid !== undefined) {
 | 
				
			||||||
 | 
					            for (let layer of gui.layerUiGrid) {
 | 
				
			||||||
 | 
					                if (screen.ratio < 0.6) {
 | 
				
			||||||
 | 
					                    layer.setColumnDefinition(1, 0.18, false);
 | 
				
			||||||
 | 
					                    layer.setColumnDefinition(2, 0.18, false);
 | 
				
			||||||
 | 
					                    layer.setColumnDefinition(3, 0.18, false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    layer.setColumnDefinition(1, 60, true);
 | 
				
			||||||
 | 
					                    layer.setColumnDefinition(2, 60, true);
 | 
				
			||||||
 | 
					                    layer.setColumnDefinition(3, 60, true);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // resize viewport based on screen size
 | 
				
			||||||
 | 
					        if (gui.layoutGrid !== undefined) {
 | 
				
			||||||
 | 
					            if (screen.ratio < 0.9) {
 | 
				
			||||||
 | 
					                gui.layoutGrid.setColumnDefinition(0, 0.5, false);
 | 
				
			||||||
 | 
					                gui.layoutGrid.setColumnDefinition(1, 0.5, false);
 | 
				
			||||||
 | 
					                camera.productWidth = 0.5;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (screen.ratio > 0.9 && screen.ratio < 1.5) {
 | 
				
			||||||
 | 
					                gui.layoutGrid.setColumnDefinition(0, 0.4, false);
 | 
				
			||||||
 | 
					                gui.layoutGrid.setColumnDefinition(1, 0.6, false);
 | 
				
			||||||
 | 
					                camera.productWidth = 0.6;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                gui.layoutGrid.setColumnDefinition(0, 0.25, false);
 | 
				
			||||||
 | 
					                gui.layoutGrid.setColumnDefinition(1, 0.75, false);
 | 
				
			||||||
 | 
					                camera.productWidth = 0.75;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            camera.product.viewport.x = 1.0 - camera.productWidth;
 | 
				
			||||||
 | 
					            camera.product.viewport.width = camera.productWidth;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create camera and lights for scene
 | 
				
			||||||
 | 
					    const lights = {};
 | 
				
			||||||
 | 
					    const env = {};
 | 
				
			||||||
 | 
					    const camera = {};
 | 
				
			||||||
 | 
					    const mask = {
 | 
				
			||||||
 | 
					        gui : 0x01000000,
 | 
				
			||||||
 | 
					        product: 0x10000000,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    async function initScene() {
 | 
				
			||||||
 | 
					        // color of scene when no skybox present
 | 
				
			||||||
 | 
					        scene.clearColor = BABYLON.Color3.FromInts(30, 30, 35);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // standard ArcRotate camera
 | 
				
			||||||
 | 
					        camera.product = new BABYLON.ArcRotateCamera("productCamera", -2.183, Math.PI/2, 1.3, new BABYLON.Vector3(0.0, 0.0, 0.0), scene);
 | 
				
			||||||
 | 
					        camera.product.minZ = 0.1;
 | 
				
			||||||
 | 
					        camera.product.lowerRadiusLimit = 0.55;
 | 
				
			||||||
 | 
					        camera.product.upperRadiusLimit = 3.0;
 | 
				
			||||||
 | 
					        camera.product.wheelDeltaPercentage = 0.1;
 | 
				
			||||||
 | 
					        camera.product.panningAxis = new BABYLON.Vector3(0.0, 0.0, 0.0);
 | 
				
			||||||
 | 
					        camera.product.attachControl(canvas, true);
 | 
				
			||||||
 | 
					        camera.product.layerMask = mask.product;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        camera.gui = new BABYLON.ArcRotateCamera("guiCamera", 0.0, 0.0, 1.0, new BABYLON.Vector3(0.0, 0.0, 0.0), scene);
 | 
				
			||||||
 | 
					        camera.gui.layerMask = mask.gui;
 | 
				
			||||||
 | 
					        setViewportSize();
 | 
				
			||||||
 | 
					        engine.onResizeObservable.add(updateViewportSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scene.activeCameras.push(camera.product);
 | 
				
			||||||
 | 
					        scene.activeCameras.push(camera.gui);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // add in IBL with linked environment
 | 
				
			||||||
 | 
					        env.lighting = BABYLON.CubeTexture.CreateFromPrefilteredData(server + "assets/env/studio.env", scene);
 | 
				
			||||||
 | 
					        env.lighting.name = "studioIBL";
 | 
				
			||||||
 | 
					        env.lighting.gammaSpace = false;
 | 
				
			||||||
 | 
					        env.lighting.rotationY = 3.14;
 | 
				
			||||||
 | 
					        scene.environmentTexture = env.lighting;
 | 
				
			||||||
 | 
					        scene.environmentIntensity = 1.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // directional light needed for shadows
 | 
				
			||||||
 | 
					        lights.dirLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(0.45, -0.34, -0.83), scene);
 | 
				
			||||||
 | 
					        lights.dirLight.position = new BABYLON.Vector3(0, 3, 5);
 | 
				
			||||||
 | 
					        lights.dirLight.intensity = 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const assets = {
 | 
				
			||||||
 | 
					        manager : new BABYLON.AssetsManager(scene),
 | 
				
			||||||
 | 
					        graphics : []
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const imprintWidth = 683;
 | 
				
			||||||
 | 
					    const imprintHeight = 2048;
 | 
				
			||||||
 | 
					    const imprint = {
 | 
				
			||||||
 | 
					        texture : new BABYLON.DynamicTexture("decalTex", {width: imprintWidth, height: imprintHeight}, scene),
 | 
				
			||||||
 | 
					        defaultBackground : new BABYLON.Color4(1.0, 0.0, 0.0, 1.0).toHexString(),
 | 
				
			||||||
 | 
					        graphics : []
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    imprint.context = imprint.texture.getContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const maxLayers = 5;
 | 
				
			||||||
 | 
					    const layers = [];
 | 
				
			||||||
 | 
					    async function loadAssets() {
 | 
				
			||||||
 | 
					        // mesh tasks
 | 
				
			||||||
 | 
					        assets.skateboard = assets.manager.addMeshTask("load skateboard", "", server + "assets/gltf/skateboard_mesh.glb")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // image tasks
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Babylon Logo", server + "assets/textures/babylonLogo.png"));
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Flame Skull", server + "assets/textures/flameSkull.png"));
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Blue Character", server + "assets/textures/cartoonHead_blue.png"));
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Orange Character", server + "assets/textures/cartoonHead_orange.png"));
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Purple Character", server + "assets/textures/cartoonHead_purple.png"));
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Pony", server + "assets/textures/pony.png"));
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Rays", server + "assets/textures/rays.png"));
 | 
				
			||||||
 | 
					        assets.graphics.push(assets.manager.addImageTask("load image_Waves", server + "assets/textures/waves.png"));
 | 
				
			||||||
 | 
					        assets.moveUpIcon = assets.manager.addImageTask("load move up icon", server + "assets/textures/upArrowCircle.svg");
 | 
				
			||||||
 | 
					        assets.moveDownIcon = assets.manager.addImageTask("load move up icon", server + "assets/textures/downArrowCircle.svg");
 | 
				
			||||||
 | 
					        assets.cancelIcon = assets.manager.addImageTask("load move up icon", server + "assets/textures/cancelCircle.svg");
 | 
				
			||||||
 | 
					        assets.addIcon = assets.manager.addImageTask("load move up icon", server + "assets/textures/addCircle.svg");
 | 
				
			||||||
 | 
					        assets.colors = [
 | 
				
			||||||
 | 
					            "Azure",
 | 
				
			||||||
 | 
					            "Black",
 | 
				
			||||||
 | 
					            "DarkGray",
 | 
				
			||||||
 | 
					            "DimGray",
 | 
				
			||||||
 | 
					            "Red",
 | 
				
			||||||
 | 
					            "FireBrick",
 | 
				
			||||||
 | 
					            "DeepPink",
 | 
				
			||||||
 | 
					            "OrangeRed",
 | 
				
			||||||
 | 
					            "Orange",
 | 
				
			||||||
 | 
					            "Gold",
 | 
				
			||||||
 | 
					            "Yellow",
 | 
				
			||||||
 | 
					            "RoyalBlue",
 | 
				
			||||||
 | 
					            "CornflowerBlue",
 | 
				
			||||||
 | 
					            "DeepSkyBlue",
 | 
				
			||||||
 | 
					            "DarkGreen",
 | 
				
			||||||
 | 
					            "ForestGreen",
 | 
				
			||||||
 | 
					            "GreenYellow",
 | 
				
			||||||
 | 
					            "RebeccaPurple",
 | 
				
			||||||
 | 
					            "DarkViolet",
 | 
				
			||||||
 | 
					            "MediumPurple",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // text tasks
 | 
				
			||||||
 | 
					        assets.decalShaderText = assets.manager.addTextFileTask("load decal node material", server + "assets/shaders/decalShader.json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // call all loading tasks
 | 
				
			||||||
 | 
					        assets.manager.load();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // task error handling
 | 
				
			||||||
 | 
					        assets.manager.onTaskErrorObservable.add((task) => {
 | 
				
			||||||
 | 
					            console.log("Error loading task: " + task.name);
 | 
				
			||||||
 | 
					            console.log(task.errorObject.message, task.errorObject.exception);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // wait for all tasks to complete
 | 
				
			||||||
 | 
					        assets.manager.onFinish = (tasks) => {
 | 
				
			||||||
 | 
					            console.log("All tasks complete!", tasks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // set graphic properties array
 | 
				
			||||||
 | 
					            for (let loaded of assets.graphics) {
 | 
				
			||||||
 | 
					                imprint.graphics.push({
 | 
				
			||||||
 | 
					                    image : loaded.image,
 | 
				
			||||||
 | 
					                    ratio : loaded.image.width / loaded.image.height,
 | 
				
			||||||
 | 
					                    size : loaded.image.width,
 | 
				
			||||||
 | 
					                    name : loaded.name.split("_")[1]
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // skateboard root
 | 
				
			||||||
 | 
					            assets.skateboardRoot = assets.skateboard.loadedMeshes[0];
 | 
				
			||||||
 | 
					            assets.skateboardRoot.name = "skateboardRoot";
 | 
				
			||||||
 | 
					            assets.skateboardMesh = assets.skateboard.loadedMeshes[1];
 | 
				
			||||||
 | 
					            for (let child of assets.skateboard.loadedMeshes) {
 | 
				
			||||||
 | 
					                child.layerMask = mask.product;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // parse loaded node material to json
 | 
				
			||||||
 | 
					            assets.decalShaderJson = JSON.parse(assets.decalShaderText.text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // set up layer data array
 | 
				
			||||||
 | 
					            for (let i = 0; i < maxLayers; i++) {
 | 
				
			||||||
 | 
					                layers.push({
 | 
				
			||||||
 | 
					                    name: undefined,
 | 
				
			||||||
 | 
					                    graphic : undefined,
 | 
				
			||||||
 | 
					                    rectangle: false,
 | 
				
			||||||
 | 
					                    ratio : 1.0,
 | 
				
			||||||
 | 
					                    horizontal : 0.0,
 | 
				
			||||||
 | 
					                    vertical : 0.0,
 | 
				
			||||||
 | 
					                    size : 512,
 | 
				
			||||||
 | 
					                    rotation : 0.0,
 | 
				
			||||||
 | 
					                    width: 1.0,
 | 
				
			||||||
 | 
					                    height : 1.0,
 | 
				
			||||||
 | 
					                    fillStyle : new BABYLON.Color4(0.0, 0.0, 0.0, 0.0).toHexString()
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // create materials for scene meshes
 | 
				
			||||||
 | 
					            createMaterials();
 | 
				
			||||||
 | 
					            createGUI();
 | 
				
			||||||
 | 
					            createContentButtons();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // prevent updating dynamic texture when setting slider values while changing layers
 | 
				
			||||||
 | 
					    let activeUpdate = true;
 | 
				
			||||||
 | 
					    // update dynamic texture
 | 
				
			||||||
 | 
					    async function updateimprint() {
 | 
				
			||||||
 | 
					        // clear imprint
 | 
				
			||||||
 | 
					        imprint.context.clearRect(0, 0, imprintWidth, imprintHeight);
 | 
				
			||||||
 | 
					        // only update dynamic texture if the user is actively changing a GUI slider
 | 
				
			||||||
 | 
					        if (activeUpdate) {
 | 
				
			||||||
 | 
					            for (let thisLayer of layers) {
 | 
				
			||||||
 | 
					                if (thisLayer.graphic !== undefined) {
 | 
				
			||||||
 | 
					                    // save context
 | 
				
			||||||
 | 
					                    imprint.context.save();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // set rotation center accounting for manual offset
 | 
				
			||||||
 | 
					                    const graphicU = thisLayer.horizontal * imprintWidth * 0.5;
 | 
				
			||||||
 | 
					                    const graphicV = thisLayer.vertical * imprintHeight * -0.5;
 | 
				
			||||||
 | 
					                    imprint.context.translate(imprintWidth * 0.5 + graphicU, imprintHeight * 0.5 + graphicV);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // determine position of graphic on texture in pixels
 | 
				
			||||||
 | 
					                    const left = thisLayer.size * thisLayer.ratio * -0.5;
 | 
				
			||||||
 | 
					                    const top = thisLayer.size * -0.5;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // rotate layer and draw graphic
 | 
				
			||||||
 | 
					                    imprint.context.rotate(thisLayer.rotation);
 | 
				
			||||||
 | 
					                    imprint.context.drawImage(thisLayer.graphic, left, top, thisLayer.ratio * thisLayer.size, thisLayer.size);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // restore context pre-rotation
 | 
				
			||||||
 | 
					                    imprint.context.restore();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (thisLayer.rectangle) {
 | 
				
			||||||
 | 
					                    // save context
 | 
				
			||||||
 | 
					                    imprint.context.save();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // set bounds of rectangle
 | 
				
			||||||
 | 
					                    const left = (1 - thisLayer.width) * 0.5 * imprintWidth;
 | 
				
			||||||
 | 
					                    const top = (1- thisLayer.height) * 0.5 * imprintHeight;
 | 
				
			||||||
 | 
					                    const width = thisLayer.width * imprintWidth;
 | 
				
			||||||
 | 
					                    const height = thisLayer.height * imprintHeight;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    const offsetU = thisLayer.horizontal * imprintWidth * 0.5;
 | 
				
			||||||
 | 
					                    const offsetV = thisLayer.vertical * imprintHeight * -0.5;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // translate center for rotation
 | 
				
			||||||
 | 
					                    const rectangleU = width * 0.5 + left;
 | 
				
			||||||
 | 
					                    const rectangleV = height * 0.5 + top;
 | 
				
			||||||
 | 
					                    imprint.context.translate(rectangleU + offsetU, rectangleV + offsetV);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // rotate context before drawing
 | 
				
			||||||
 | 
					                    imprint.context.rotate(thisLayer.rotation);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // draw rectangle
 | 
				
			||||||
 | 
					                    imprint.context.fillStyle = thisLayer.fillStyle;
 | 
				
			||||||
 | 
					                    imprint.context.fillRect(-width * 0.5, -height * 0.5, width, height);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                    // restore context
 | 
				
			||||||
 | 
					                    imprint.context.restore();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // update texture
 | 
				
			||||||
 | 
					            imprint.texture.update();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create materials for scene meshes
 | 
				
			||||||
 | 
					    // ignore textures embedded in shader when loading
 | 
				
			||||||
 | 
					    BABYLON.NodeMaterial.IgnoreTexturesAtLoadTime = true;
 | 
				
			||||||
 | 
					    const meshesMats = {};
 | 
				
			||||||
 | 
					    async function createMaterials() {
 | 
				
			||||||
 | 
					        meshesMats.skateboard = BABYLON.NodeMaterial.Parse(assets.decalShaderJson, scene);
 | 
				
			||||||
 | 
					        meshesMats.skateboard.name = "skateboardDecalMat";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // create default dynamic texture
 | 
				
			||||||
 | 
					        updateimprint();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // get texture blocks and assign PBR textures from loaded mesh and dynamic texture
 | 
				
			||||||
 | 
					        meshesMats.skateboard.getBlockByName("decalTex").texture = imprint.texture;
 | 
				
			||||||
 | 
					        meshesMats.skateboard.getBlockByName("baseColorTex").texture = assets.skateboardMesh.material.albedoTexture;
 | 
				
			||||||
 | 
					        meshesMats.skateboard.getBlockByName("ormTex").texture = assets.skateboardMesh.material.metallicTexture;
 | 
				
			||||||
 | 
					        meshesMats.skateboard.getBlockByName("normalTex").texture = assets.skateboardMesh.material.bumpTexture;
 | 
				
			||||||
 | 
					        assets.skateboardMesh.material.dispose();
 | 
				
			||||||
 | 
					        assets.skateboardMesh.material = meshesMats.skateboard;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create GUI AdvancedDynamicTexture
 | 
				
			||||||
 | 
					    const gui = {
 | 
				
			||||||
 | 
					        texture : await BABYLON.GUI.AdvancedDynamicTexture.ParseFromFileAsync(server + "assets/gui/imprintCustomizationGui.json"),
 | 
				
			||||||
 | 
					        contentButtons : []
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    // dynamically create content buttons 
 | 
				
			||||||
 | 
					    function createContentButtons() {
 | 
				
			||||||
 | 
					        let lastButton = 0;
 | 
				
			||||||
 | 
					        // create color buttons from colors array
 | 
				
			||||||
 | 
					        for (let i = 0; i < assets.colors.length; i++) {
 | 
				
			||||||
 | 
					            const button = new BABYLON.GUI.Button(assets.colors[i]);
 | 
				
			||||||
 | 
					            button.fixedRatio = 1.0;
 | 
				
			||||||
 | 
					            button.thickness = 2;
 | 
				
			||||||
 | 
					            button.background = assets.colors[i];
 | 
				
			||||||
 | 
					            button.onPointerClickObservable.add(function() {
 | 
				
			||||||
 | 
					                layers[currentLayer].name = button.name;
 | 
				
			||||||
 | 
					                layers[currentLayer].fillStyle = button.name;
 | 
				
			||||||
 | 
					                layers[currentLayer].rectangle = true;
 | 
				
			||||||
 | 
					                layers[currentLayer].graphic = undefined;
 | 
				
			||||||
 | 
					                layers[currentLayer].width = 1.0;
 | 
				
			||||||
 | 
					                loadLayerContentUI(currentLayer);
 | 
				
			||||||
 | 
					                updateLayerNames();
 | 
				
			||||||
 | 
					                updateGUI(layers[currentLayer]);
 | 
				
			||||||
 | 
					                showLayerControls();
 | 
				
			||||||
 | 
					                updateimprint();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            gui.contentButtonsGrid.addControl(button, Math.floor(i / 4), i % 4);
 | 
				
			||||||
 | 
					            gui.contentButtons.push(button);
 | 
				
			||||||
 | 
					            lastButton = i + 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // create graphic buttons from graphics array
 | 
				
			||||||
 | 
					        for (let i = lastButton; i < assets.graphics.length + lastButton; i++) {
 | 
				
			||||||
 | 
					            const button = BABYLON.GUI.Button.CreateImageOnlyButton(assets.graphics[i - lastButton].name.split("_")[1], assets.graphics[i - lastButton].url);
 | 
				
			||||||
 | 
					            button.image.width = 0.8;
 | 
				
			||||||
 | 
					            button.image.height = 0.8;
 | 
				
			||||||
 | 
					            button.image.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
 | 
				
			||||||
 | 
					            button.image.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER;
 | 
				
			||||||
 | 
					            button.fixedRatio = 1.0;
 | 
				
			||||||
 | 
					            button.thickness = 2;
 | 
				
			||||||
 | 
					            button.background = "black";
 | 
				
			||||||
 | 
					            button.onPointerClickObservable.add(function() {
 | 
				
			||||||
 | 
					                layers[currentLayer].name = button.name;
 | 
				
			||||||
 | 
					                layers[currentLayer].fillStyle = undefined;
 | 
				
			||||||
 | 
					                layers[currentLayer].rectangle = false;
 | 
				
			||||||
 | 
					                for (let graphic of assets.graphics) {
 | 
				
			||||||
 | 
					                    if (graphic.name.split("_")[1] === button.name) {
 | 
				
			||||||
 | 
					                        layers[currentLayer].graphic = graphic.image;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                loadLayerContentUI(currentLayer);
 | 
				
			||||||
 | 
					                updateLayerNames();
 | 
				
			||||||
 | 
					                updateGUI(layers[currentLayer]);
 | 
				
			||||||
 | 
					                showLayerControls();
 | 
				
			||||||
 | 
					                updateimprint();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            gui.contentButtonsGrid.addControl(button, Math.floor(i / 4), i % 4);
 | 
				
			||||||
 | 
					            gui.contentButtons.push(button);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        updateViewportSize();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // change visibility of GUI grids to show content buttons
 | 
				
			||||||
 | 
					    let currentLayer = undefined;
 | 
				
			||||||
 | 
					    function chooseContent() {
 | 
				
			||||||
 | 
					        updateActiveLayer();
 | 
				
			||||||
 | 
					        gui.slidersGrid.isVisible = false;
 | 
				
			||||||
 | 
					        gui.contentButtonsGrid.isVisible = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // change visibility of GUI grids to show layer parameter sliders
 | 
				
			||||||
 | 
					    function showLayerControls() {
 | 
				
			||||||
 | 
					        updateActiveLayer();
 | 
				
			||||||
 | 
					        gui.slidersGrid.isVisible = true;
 | 
				
			||||||
 | 
					        gui.contentButtonsGrid.isVisible = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // update GUI layer background color to highlight active layer
 | 
				
			||||||
 | 
					    function updateActiveLayer() {
 | 
				
			||||||
 | 
					        updateGUI(layers[currentLayer]);
 | 
				
			||||||
 | 
					        for (let rect of gui.layerBG) {
 | 
				
			||||||
 | 
					            if (parseInt(rect.name.split("_")[1], 10) === currentLayer) {
 | 
				
			||||||
 | 
					                rect.background = gui.highlightLayerColor;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                rect.background = gui.layerColor;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // update layers array to move layer parameters up or down one index position
 | 
				
			||||||
 | 
					    function moveLayer(index, direction) {
 | 
				
			||||||
 | 
					        const temp = layers[index];
 | 
				
			||||||
 | 
					        if (direction === "up") {
 | 
				
			||||||
 | 
					            layers[index] = layers[index - 1];
 | 
				
			||||||
 | 
					            layers[index - 1] = temp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (direction === "down") {
 | 
				
			||||||
 | 
					            layers[index] = layers[index + 1];
 | 
				
			||||||
 | 
					            layers[index + 1] = temp;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        updateGUI(layers[currentLayer]);
 | 
				
			||||||
 | 
					        updateimprint();
 | 
				
			||||||
 | 
					        updateLayerNames();
 | 
				
			||||||
 | 
					        gui.slidersGrid.isVisible = false;
 | 
				
			||||||
 | 
					        gui.contentButtonsGrid.isVisible = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // update GUI layer names to match layers array
 | 
				
			||||||
 | 
					    function updateLayerNames() {
 | 
				
			||||||
 | 
					        for (let i=0; i < layers.length; i++) {
 | 
				
			||||||
 | 
					            for (let target of gui.layerUiGrid) {
 | 
				
			||||||
 | 
					                if (parseInt(target.name.split("_")[1]) === i) {
 | 
				
			||||||
 | 
					                    for (let control of target.getDescendants(false)) {
 | 
				
			||||||
 | 
					                        if (control.name.split("_")[2] === "contentName") {
 | 
				
			||||||
 | 
					                            if (layers[i].name !== undefined)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                loadLayerContentUI(i);
 | 
				
			||||||
 | 
					                                control.text = layers[i].name;
 | 
				
			||||||
 | 
					                            }  
 | 
				
			||||||
 | 
					                            else {
 | 
				
			||||||
 | 
					                                unloadLayerContentUI(i);
 | 
				
			||||||
 | 
					                            }                  
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // update layer to hide insert button and show layer action buttons when content selected for layer
 | 
				
			||||||
 | 
					    function loadLayerContentUI(index) {
 | 
				
			||||||
 | 
					        for (let button of gui.buttons) {
 | 
				
			||||||
 | 
					            if (button.name.split("_")[2] === "insertButton" && button.name.split("_")[1] === index.toString()) {
 | 
				
			||||||
 | 
					                button.isVisible = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (let grid of gui.layerUiGrid) {
 | 
				
			||||||
 | 
					            if (grid.name.split("_")[1] === index.toString()) {
 | 
				
			||||||
 | 
					                grid.isVisible = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // update layer to hide layer action buttons and show insert button when content deleted from layer
 | 
				
			||||||
 | 
					    function unloadLayerContentUI(index) {
 | 
				
			||||||
 | 
					        for (let button of gui.buttons) {
 | 
				
			||||||
 | 
					            if (button.name.split("_")[2] === "insertButton" && button.name.split("_")[1] === index.toString()) {
 | 
				
			||||||
 | 
					                button.isVisible = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (let grid of gui.layerUiGrid) {
 | 
				
			||||||
 | 
					            if (grid.name.split("_")[1] === index.toString()) {
 | 
				
			||||||
 | 
					                grid.isVisible = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // revert layer parameters in layers array to default values when content deleted from layer
 | 
				
			||||||
 | 
					    function removeLayerGraphic(index) {
 | 
				
			||||||
 | 
					        layers[index].name = undefined;
 | 
				
			||||||
 | 
					        layers[index].graphic = undefined;
 | 
				
			||||||
 | 
					        layers[index].rectangle = false;
 | 
				
			||||||
 | 
					        layers[index].ratio = 1.0;
 | 
				
			||||||
 | 
					        layers[index].horizontal = 0.0;
 | 
				
			||||||
 | 
					        layers[index].vertical = 0.0;
 | 
				
			||||||
 | 
					        layers[index].size = 512;
 | 
				
			||||||
 | 
					        layers[index].rotation = 0.0;
 | 
				
			||||||
 | 
					        layers[index].width = 1.0;
 | 
				
			||||||
 | 
					        layers[index].height = 1.0;
 | 
				
			||||||
 | 
					        layers[index].fillStyle = new BABYLON.Color4(0.0, 0.0, 0.0, 0.0).toHexString();
 | 
				
			||||||
 | 
					        currentLayer = undefined;
 | 
				
			||||||
 | 
					        updateActiveLayer();
 | 
				
			||||||
 | 
					        gui.slidersGrid.isVisible = false;
 | 
				
			||||||
 | 
					        gui.contentButtonsGrid.isVisible = false;
 | 
				
			||||||
 | 
					        updateimprint();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // define GUI style parameters and actions for interactivity
 | 
				
			||||||
 | 
					    gui.texture.layer.layerMask = mask.gui;
 | 
				
			||||||
 | 
					    gui.layerColor = "#381A1AFF";
 | 
				
			||||||
 | 
					    gui.highlightLayerColor = "#e42d2dFF";
 | 
				
			||||||
 | 
					    gui.layerUiGrid = [];
 | 
				
			||||||
 | 
					    async function createGUI() {
 | 
				
			||||||
 | 
					        // get grid controls and set pointer blocker to false
 | 
				
			||||||
 | 
					        for (let grid of gui.texture.getControlsByType("Grid")) {
 | 
				
			||||||
 | 
					            grid.isPointerBlocker = false;
 | 
				
			||||||
 | 
					            if (grid.name === "layout") {
 | 
				
			||||||
 | 
					                gui.layoutGrid = grid;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (grid.name === "layers") {
 | 
				
			||||||
 | 
					                gui.layersGrid = grid;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (grid.name === "sliders") {
 | 
				
			||||||
 | 
					                gui.slidersGrid = grid;
 | 
				
			||||||
 | 
					                gui.slidersGrid.isVisible = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (grid.name === "contentButtons") {
 | 
				
			||||||
 | 
					                gui.contentButtonsGrid = grid;
 | 
				
			||||||
 | 
					                gui.contentButtonsGrid.isVisible = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (grid.name.split("_")[2] === "uiGrid") {
 | 
				
			||||||
 | 
					                gui.layerUiGrid.push(grid);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // get rectangle controls and set pointer blocker to false
 | 
				
			||||||
 | 
					        for (let rect of gui.texture.getControlsByType("Rectangle")) {
 | 
				
			||||||
 | 
					            rect.isPointerBlocker = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // get slider title and slider controls and set alignment on text for all avalable parameters
 | 
				
			||||||
 | 
					        gui.slider_horizontal_title = gui.texture.getControlByName("slider_horizontal_title");
 | 
				
			||||||
 | 
					        gui.slider_horizontal_title.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
 | 
				
			||||||
 | 
					        gui.slider_horizontal = gui.texture.getControlByName("slider_horizontal");
 | 
				
			||||||
 | 
					        gui.slider_horizontal.onValueChangedObservable.add(function(value) {
 | 
				
			||||||
 | 
					            layers[currentLayer].horizontal = value;
 | 
				
			||||||
 | 
					            if (activeUpdate) {
 | 
				
			||||||
 | 
					                updateimprint();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gui.slider_vertical_title = gui.texture.getControlByName("slider_vertical_title");
 | 
				
			||||||
 | 
					        gui.slider_vertical_title.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
 | 
				
			||||||
 | 
					        gui.slider_vertical = gui.texture.getControlByName("slider_vertical");
 | 
				
			||||||
 | 
					        gui.slider_vertical.onValueChangedObservable.add(function(value) {
 | 
				
			||||||
 | 
					            layers[currentLayer].vertical = value;
 | 
				
			||||||
 | 
					            if (activeUpdate) {
 | 
				
			||||||
 | 
					                updateimprint();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gui.slider_size_title = gui.texture.getControlByName("slider_size_title");
 | 
				
			||||||
 | 
					        gui.slider_size_title.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
 | 
				
			||||||
 | 
					        gui.slider_size = gui.texture.getControlByName("slider_size");
 | 
				
			||||||
 | 
					        gui.slider_size.onValueChangedObservable.add(function(value) {
 | 
				
			||||||
 | 
					            layers[currentLayer].size = value;
 | 
				
			||||||
 | 
					            if (activeUpdate) {
 | 
				
			||||||
 | 
					                updateimprint();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gui.slider_width_title = gui.texture.getControlByName("slider_width_title");
 | 
				
			||||||
 | 
					        gui.slider_width_title.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
 | 
				
			||||||
 | 
					        gui.slider_width = gui.texture.getControlByName("slider_width");
 | 
				
			||||||
 | 
					        gui.slider_width.onValueChangedObservable.add(function(value) {
 | 
				
			||||||
 | 
					            layers[currentLayer].width = value;
 | 
				
			||||||
 | 
					            if (activeUpdate) {
 | 
				
			||||||
 | 
					                updateimprint();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gui.slider_height_title = gui.texture.getControlByName("slider_height_title");
 | 
				
			||||||
 | 
					        gui.slider_height_title.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
 | 
				
			||||||
 | 
					        gui.slider_height = gui.texture.getControlByName("slider_height");
 | 
				
			||||||
 | 
					        gui.slider_height.onValueChangedObservable.add(function(value) {
 | 
				
			||||||
 | 
					            if (layers[currentLayer].rectangle) {
 | 
				
			||||||
 | 
					                layers[currentLayer].height = value;
 | 
				
			||||||
 | 
					                if (activeUpdate) {
 | 
				
			||||||
 | 
					                    updateimprint();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                layers[currentLayer].rotation = value;
 | 
				
			||||||
 | 
					                if (activeUpdate) {
 | 
				
			||||||
 | 
					                    updateimprint();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gui.slider_rotation_title = gui.texture.getControlByName("slider_rotation_title");
 | 
				
			||||||
 | 
					        gui.slider_rotation_title.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
 | 
				
			||||||
 | 
					        gui.slider_rotation = gui.texture.getControlByName("slider_rotation");
 | 
				
			||||||
 | 
					        gui.slider_rotation.onValueChangedObservable.add(function(value) {
 | 
				
			||||||
 | 
					            layers[currentLayer].rotation = value;
 | 
				
			||||||
 | 
					            if (activeUpdate) {
 | 
				
			||||||
 | 
					                updateimprint();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // get layers backgrounds
 | 
				
			||||||
 | 
					        gui.layerBG = [];
 | 
				
			||||||
 | 
					        for (let rect of gui.texture.getControlsByType("Rectangle")) {
 | 
				
			||||||
 | 
					            if (rect.name.split("_")[2] === "bg") {
 | 
				
			||||||
 | 
					                gui.layerBG.push(rect);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // assign icons and actions to buttons
 | 
				
			||||||
 | 
					        gui.buttons = gui.texture.getControlsByType("Button");
 | 
				
			||||||
 | 
					        for (let button of gui.buttons) {
 | 
				
			||||||
 | 
					            button.width = "100%";
 | 
				
			||||||
 | 
					            button.fixedRatio = 1.0;
 | 
				
			||||||
 | 
					            if (button._children[0] !== undefined) {
 | 
				
			||||||
 | 
					                button._children[0].height = "100%";
 | 
				
			||||||
 | 
					                button._children[0].fixedRatio = 1.0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (button.name.split("_")[2] === "upButton") {
 | 
				
			||||||
 | 
					                button._children[0].source = assets.moveUpIcon.url;
 | 
				
			||||||
 | 
					                button.isVisible = true;
 | 
				
			||||||
 | 
					                button.onPointerClickObservable.add(function() {
 | 
				
			||||||
 | 
					                    moveLayer(parseInt(button.name.split("_")[1]), "up");
 | 
				
			||||||
 | 
					                    currentLayer = undefined;
 | 
				
			||||||
 | 
					                    updateActiveLayer();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (button.name.split("_")[2] === "downButton") {
 | 
				
			||||||
 | 
					                button._children[0].source = assets.moveDownIcon.url;
 | 
				
			||||||
 | 
					                button.isVisible = true;
 | 
				
			||||||
 | 
					                button.onPointerClickObservable.add(function() {
 | 
				
			||||||
 | 
					                    moveLayer(parseInt(button.name.split("_")[1]), "down");
 | 
				
			||||||
 | 
					                    currentLayer = undefined;
 | 
				
			||||||
 | 
					                    updateActiveLayer();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (button.name.split("_")[2] === "insertButton") {
 | 
				
			||||||
 | 
					                button._children[0].source = assets.addIcon.url;
 | 
				
			||||||
 | 
					                button.onPointerClickObservable.add(function() {
 | 
				
			||||||
 | 
					                    currentLayer = parseInt(button.name.split("_")[1]);
 | 
				
			||||||
 | 
					                    chooseContent();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (button.name.split("_")[2] === "deleteButton") {
 | 
				
			||||||
 | 
					                button._children[0].source = assets.cancelIcon.url;
 | 
				
			||||||
 | 
					                button.isVisible = true;
 | 
				
			||||||
 | 
					                button.onPointerClickObservable.add(function() {
 | 
				
			||||||
 | 
					                    unloadLayerContentUI(parseInt(button.name.split("_")[1]));
 | 
				
			||||||
 | 
					                    removeLayerGraphic(parseInt(button.name.split("_")[1]))
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (button.name.split("_")[2] === "selectButton") {
 | 
				
			||||||
 | 
					                button.fixedRatio = 0.0;
 | 
				
			||||||
 | 
					                button.onPointerClickObservable.add(function() {
 | 
				
			||||||
 | 
					                    currentLayer = parseInt(button.name.split("_")[1]);
 | 
				
			||||||
 | 
					                    updateActiveLayer();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                button.isVisible = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // get layer textblocks and assign them to the layer array in the correct order
 | 
				
			||||||
 | 
					        gui.layerContentNames = [];
 | 
				
			||||||
 | 
					        for (let textBlock of gui.texture.getControlsByType("TextBlock")) {
 | 
				
			||||||
 | 
					            if (textBlock.name.split("_")[2] === "contentName") {
 | 
				
			||||||
 | 
					                gui.layerContentNames.push(textBlock);
 | 
				
			||||||
 | 
					                textBlock.isVisible = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // update sliders panel with the correct controls for the layer
 | 
				
			||||||
 | 
					    function updateGUI(layer) {
 | 
				
			||||||
 | 
					        if (layer !== undefined) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (currentLayer !== undefined) {
 | 
				
			||||||
 | 
					            // disable update for dynamic texture when setting sliders due to layer change
 | 
				
			||||||
 | 
					            activeUpdate = false;
 | 
				
			||||||
 | 
					            gui.slidersGrid.isVisible = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // update sliders for rectangle content parameters
 | 
				
			||||||
 | 
					            if (layer.rectangle && layer.graphic === undefined) {
 | 
				
			||||||
 | 
					                gui.slider_horizontal.minimum = -1;
 | 
				
			||||||
 | 
					                gui.slider_horizontal.maximum = 1;
 | 
				
			||||||
 | 
					                gui.slider_horizontal.value = layer.horizontal;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                gui.slider_vertical.minimum = -1;
 | 
				
			||||||
 | 
					                gui.slider_vertical.maximum = 1;
 | 
				
			||||||
 | 
					                gui.slider_vertical.value = layer.vertical;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                gui.slider_size_title.isVisible = false;
 | 
				
			||||||
 | 
					                gui.slider_size.isVisible = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                gui.slider_width_title.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_width.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_width.minimum = 0;
 | 
				
			||||||
 | 
					                gui.slider_width.maximum = 2;
 | 
				
			||||||
 | 
					                gui.slider_width.value = layer.width;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                gui.slider_height_title.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_height.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_height.minimum = 0;
 | 
				
			||||||
 | 
					                gui.slider_height.maximum = 2;
 | 
				
			||||||
 | 
					                gui.slider_height.value = layer.height;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                gui.slidersGrid.removeControl(gui.slider_rotation_title)
 | 
				
			||||||
 | 
					                gui.slidersGrid.addControl(gui.slider_rotation_title, 5, 0)
 | 
				
			||||||
 | 
					                gui.slidersGrid.removeControl(gui.slider_rotation)
 | 
				
			||||||
 | 
					                gui.slidersGrid.addControl(gui.slider_rotation, 5, 1)
 | 
				
			||||||
 | 
					                gui.slider_rotation_title.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_rotation.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_rotation.minimum = -Math.PI;
 | 
				
			||||||
 | 
					                gui.slider_rotation.maximum = Math.PI;
 | 
				
			||||||
 | 
					                gui.slider_rotation.value = layer.rotation;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // update sliders for image content parameters
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                gui.slider_horizontal.minimum = -2;
 | 
				
			||||||
 | 
					                gui.slider_horizontal.maximum = 2;
 | 
				
			||||||
 | 
					                gui.slider_horizontal.value = layer.horizontal;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                gui.slider_vertical.minimum = -2;
 | 
				
			||||||
 | 
					                gui.slider_vertical.maximum = 2;
 | 
				
			||||||
 | 
					                gui.slider_vertical.value = layer.vertical;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                gui.slider_size_title.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_size.isVisible = true;
 | 
				
			||||||
 | 
					                gui.slider_size.minimum = 0;
 | 
				
			||||||
 | 
					                gui.slider_size.maximum = 2048;
 | 
				
			||||||
 | 
					                gui.slider_size.value = layer.size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                gui.slider_width_title.isVisible = false;
 | 
				
			||||||
 | 
					                gui.slider_width.isVisible = false;
 | 
				
			||||||
 | 
					                gui.slider_height_title.isVisible = false;
 | 
				
			||||||
 | 
					                gui.slider_height.isVisible = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                gui.slidersGrid.removeControl(gui.slider_rotation_title)
 | 
				
			||||||
 | 
					                gui.slidersGrid.addControl(gui.slider_rotation_title, 4, 0)
 | 
				
			||||||
 | 
					                gui.slidersGrid.removeControl(gui.slider_rotation)
 | 
				
			||||||
 | 
					                gui.slidersGrid.addControl(gui.slider_rotation, 4, 1)
 | 
				
			||||||
 | 
					                gui.slider_rotation.minimum = -Math.PI;
 | 
				
			||||||
 | 
					                gui.slider_rotation.maximum = Math.PI;
 | 
				
			||||||
 | 
					                gui.slider_rotation.value = layer.rotation;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            activeUpdate = true;
 | 
				
			||||||
 | 
					            }        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // scene logic
 | 
				
			||||||
 | 
					    initScene();
 | 
				
			||||||
 | 
					    await loadAssets();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return scene;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export default createScene
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/main.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import { createApp } from 'vue'
 | 
				
			||||||
 | 
					import App from './App.vue'
 | 
				
			||||||
 | 
					import router from './router'
 | 
				
			||||||
 | 
					import vuetify from './plugins/vuetify'
 | 
				
			||||||
 | 
					import '@/assets/style/normal.css'
 | 
				
			||||||
 | 
					import '@/assets/style/components.css'
 | 
				
			||||||
 | 
					import '@/assets/style/global.css'
 | 
				
			||||||
 | 
					import '@/assets/style/media.css'
 | 
				
			||||||
 | 
					import pinia from './stores'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 创建Vue应用实例
 | 
				
			||||||
 | 
					const app = createApp(App)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.use(pinia)
 | 
				
			||||||
 | 
					app.use(router)
 | 
				
			||||||
 | 
					app.use(vuetify)
 | 
				
			||||||
 | 
					app.mount('#app')
 | 
				
			||||||
							
								
								
									
										178
									
								
								src/pages/MainApp.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,178 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div class="main-app">
 | 
				
			||||||
 | 
					        <!-- 3D视图背景层 -->
 | 
				
			||||||
 | 
					        <View ref="ViewRef" class="view-layer"></View>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- UI覆盖层 -->
 | 
				
			||||||
 | 
					        <CustomizationIndex class="ui-layer"></CustomizationIndex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 材质颜色选择器 -->
 | 
				
			||||||
 | 
					        <!-- <MaterialColorPicker></MaterialColorPicker> -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import { ref, onMounted, watch, nextTick } from "vue";
 | 
				
			||||||
 | 
					import View from "./View.vue";
 | 
				
			||||||
 | 
					import CustomizationIndex from "@/components/CustomizationIndex.vue";
 | 
				
			||||||
 | 
					import { useCustomizationStore } from "@/stores/customization";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 组件引用
 | 
				
			||||||
 | 
					const ViewRef = ref();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 状态管理
 | 
				
			||||||
 | 
					const isSceneReady = ref(false);
 | 
				
			||||||
 | 
					const isLoading = ref(false);
 | 
				
			||||||
 | 
					const error = ref<string | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        isLoading.value = true;
 | 
				
			||||||
 | 
					        error.value = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.log('开始初始化应用');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 创建场景
 | 
				
			||||||
 | 
					        await ViewRef.value.createScene();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 加载模型和动画
 | 
				
			||||||
 | 
					        await loadModelsAndAnimations();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 等待场景完全准备就绪
 | 
				
			||||||
 | 
					        await nextTick();
 | 
				
			||||||
 | 
					        isSceneReady.value = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.log('应用初始化完成');
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					        console.error('应用初始化失败:', err);
 | 
				
			||||||
 | 
					        error.value = `初始化失败: ${err}`;
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					        isLoading.value = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 加载模型和动画
 | 
				
			||||||
 | 
					const loadModelsAndAnimations = async () => {
 | 
				
			||||||
 | 
					    const modelUrls = [
 | 
				
			||||||
 | 
					        "/model/skateboard_mesh.glb",
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const animationUrls = ["/1100_HD_Exp/SpinAround_F.glb"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return new Promise<void>((resolve, reject) => {
 | 
				
			||||||
 | 
					        ViewRef.value.loadModel(
 | 
				
			||||||
 | 
					            modelUrls,
 | 
				
			||||||
 | 
					            animationUrls,
 | 
				
			||||||
 | 
					            async () => {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    console.log('模型和动画加载完成');
 | 
				
			||||||
 | 
					                    resolve();
 | 
				
			||||||
 | 
					                } catch (err) {
 | 
				
			||||||
 | 
					                    reject(err);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.main-app {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.view-layer {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    z-index: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ui-layer {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    pointer-events: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 让UI层中的交互元素可以点击 */
 | 
				
			||||||
 | 
					.ui-layer :deep(.control-panel),
 | 
				
			||||||
 | 
					.ui-layer :deep(.top-toolbar),
 | 
				
			||||||
 | 
					.ui-layer :deep(.bottom-controls) {
 | 
				
			||||||
 | 
					    pointer-events: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.truck-viewer-btn {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    right: 20px;
 | 
				
			||||||
 | 
					    top: 70px;
 | 
				
			||||||
 | 
					    /* 位于视频按钮下方 */
 | 
				
			||||||
 | 
					    padding: 8px 15px;
 | 
				
			||||||
 | 
					    background-color: rgba(0, 0, 0, 0.6);
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					    border: 1px solid white;
 | 
				
			||||||
 | 
					    border-radius: 5px;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    transition: all 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.truck-viewer-btn:hover {
 | 
				
			||||||
 | 
					    background-color: rgba(255, 255, 255, 0.8);
 | 
				
			||||||
 | 
					    color: black;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hotspot-box {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 350px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 卖点信息按钮样式 */
 | 
				
			||||||
 | 
					.feature-info-btn {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    right: 20px;
 | 
				
			||||||
 | 
					    bottom: 20px;
 | 
				
			||||||
 | 
					    padding: 8px 15px;
 | 
				
			||||||
 | 
					    background-color: rgba(0, 0, 0, 0.6);
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					    border: 1px solid white;
 | 
				
			||||||
 | 
					    border-radius: 5px;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    transition: all 0.2s;
 | 
				
			||||||
 | 
					    z-index: 100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.feature-info-btn:hover {
 | 
				
			||||||
 | 
					    background-color: rgba(255, 255, 255, 0.8);
 | 
				
			||||||
 | 
					    color: black;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.feature-info-content {
 | 
				
			||||||
 | 
					    color: #333;
 | 
				
			||||||
 | 
					    line-height: 1.6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.feature-info-content p {
 | 
				
			||||||
 | 
					    margin: 2px 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 测试面板样式 */
 | 
				
			||||||
 | 
					.test-panel {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    bottom: 20px;
 | 
				
			||||||
 | 
					    left: 20px;
 | 
				
			||||||
 | 
					    z-index: 1000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/pages/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					# Pages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Vue components created in this folder will automatically be converted to navigatable routes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository.
 | 
				
			||||||
							
								
								
									
										103
									
								
								src/pages/View.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="view-wrapper">
 | 
				
			||||||
 | 
					    <div class="app-view">
 | 
				
			||||||
 | 
					      <canvas id="renderDom"></canvas>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import MainEditor from "@/script/MainEditor";
 | 
				
			||||||
 | 
					import { useCustomizationStore } from '@/stores/customization';
 | 
				
			||||||
 | 
					import { onMounted } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let mainEditor = new MainEditor();
 | 
				
			||||||
 | 
					const customizationStore = useCustomizationStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 初始化定制化store
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  customizationStore.initializeLayers();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const createScene = async () => {
 | 
				
			||||||
 | 
					  await mainEditor.Awake();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const loadModel = async (modelUrlList: Array<string>,animationUrlList: Array<string>, complete: Function) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mainEditor.loadModel({
 | 
				
			||||||
 | 
					    modelUrlList:modelUrlList,
 | 
				
			||||||
 | 
					    animationUrlList:animationUrlList,
 | 
				
			||||||
 | 
					    success: () => {
 | 
				
			||||||
 | 
					      complete();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    error: (e: any) => {
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const loadAnimation = async (animationUrlList: Array<string>, complete: Function) => {
 | 
				
			||||||
 | 
					  mainEditor.loadAnimation({
 | 
				
			||||||
 | 
					    animationUrlList:animationUrlList,
 | 
				
			||||||
 | 
					    success: () => {
 | 
				
			||||||
 | 
					      complete();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineExpose({
 | 
				
			||||||
 | 
					  createScene,
 | 
				
			||||||
 | 
					  loadModel,
 | 
				
			||||||
 | 
					  loadAnimation,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.app-view {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  /* background-color: #efeff1; */
 | 
				
			||||||
 | 
					  /* background: url("/resources/微信图片_20250512185835.jpg") no-repeat center center fixed; */
 | 
				
			||||||
 | 
					  background-size: 100% 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#renderDom {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#button {
 | 
				
			||||||
 | 
					  width: 100px;
 | 
				
			||||||
 | 
					  height: 60px;
 | 
				
			||||||
 | 
					  background-color: red;
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  right: 0;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#mask {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  z-index: 99;
 | 
				
			||||||
 | 
					  pointer-events: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.time {
 | 
				
			||||||
 | 
					  width: 150px;
 | 
				
			||||||
 | 
					  height: 40px;
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 10px;
 | 
				
			||||||
 | 
					  background-color: red;
 | 
				
			||||||
 | 
					  left: 50%;
 | 
				
			||||||
 | 
					  margin-left: -75px;
 | 
				
			||||||
 | 
					  z-index: 99;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  font-size: 20px;
 | 
				
			||||||
 | 
					  line-height: 40px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										3
									
								
								src/plugins/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					# Plugins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally.
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/plugins/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * plugins/index.ts
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Automatically included in `./src/main.ts`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Plugins
 | 
				
			||||||
 | 
					import vuetify from './vuetify'
 | 
				
			||||||
 | 
					import router from '../router'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Types
 | 
				
			||||||
 | 
					import type { App } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function registerPlugins (app: App) {
 | 
				
			||||||
 | 
					  app
 | 
				
			||||||
 | 
					    .use(vuetify)
 | 
				
			||||||
 | 
					    .use(router)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/plugins/vuetify.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * plugins/vuetify.ts
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Framework documentation: https://vuetifyjs.com`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Styles
 | 
				
			||||||
 | 
					import '@mdi/font/css/materialdesignicons.css'
 | 
				
			||||||
 | 
					import 'vuetify/styles'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Composables
 | 
				
			||||||
 | 
					import { createVuetify } from 'vuetify'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
 | 
				
			||||||
 | 
					export default createVuetify({
 | 
				
			||||||
 | 
					  theme: {
 | 
				
			||||||
 | 
					    defaultTheme: 'system',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/router/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import { createRouter, createWebHashHistory } from 'vue-router'
 | 
				
			||||||
 | 
					import MainApp from '@/pages/MainApp.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes = [
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/',
 | 
				
			||||||
 | 
					    name: 'home',
 | 
				
			||||||
 | 
					    component: MainApp,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/:pathMatch(.*)*', // 匹配所有未定义路由
 | 
				
			||||||
 | 
					    name: 'NotFound',
 | 
				
			||||||
 | 
					    component: MainApp,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const router = createRouter({
 | 
				
			||||||
 | 
					  history: createWebHashHistory(), // hash 模式
 | 
				
			||||||
 | 
					  routes,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default router
 | 
				
			||||||
							
								
								
									
										116
									
								
								src/script/MainEditor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					import { AppConfig, MainApp } from '@/script/core'
 | 
				
			||||||
 | 
					import { Vector3 } from '@babylonjs/core'
 | 
				
			||||||
 | 
					class MainEditor {
 | 
				
			||||||
 | 
					  mainApp!: MainApp
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.mainApp = new MainApp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  async Awake() {
 | 
				
			||||||
 | 
					    this.mainApp.Awake()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   loadModel(config: AppConfig){
 | 
				
			||||||
 | 
					      this.mainApp.loadAConfig(config)
 | 
				
			||||||
 | 
					      this.mainApp.loadModel()
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loadAnimation(config: AppConfig){
 | 
				
			||||||
 | 
					    this.mainApp.loadAConfig(config)
 | 
				
			||||||
 | 
					    this.mainApp.loadAnimation()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param from 从哪开始
 | 
				
			||||||
 | 
					   * @param to 从哪结束
 | 
				
			||||||
 | 
					   * @param speed 速度
 | 
				
			||||||
 | 
					   * @returns
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //分辨率等级
 | 
				
			||||||
 | 
					  setLevel(level: number) {
 | 
				
			||||||
 | 
					   // this.mainApp.appEngin.object.setHardwareScalingLevel(level)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onClickModel(callback: Function) {
 | 
				
			||||||
 | 
					   // this.mainApp.appRay.onPointerDown(callback)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 重置相机位置
 | 
				
			||||||
 | 
					  resetCamera() {
 | 
				
			||||||
 | 
					    if (this.mainApp.appCamera && this.mainApp.appCamera.object) {
 | 
				
			||||||
 | 
					      // 重置到初始位置
 | 
				
			||||||
 | 
					      // this.mainApp.appCamera.object.setPosition(new Vector3(0, 0, 6))
 | 
				
			||||||
 | 
					      // this.mainApp.appCamera.setTarget(0, 0, 0)
 | 
				
			||||||
 | 
					      console.log('相机已重置到初始位置')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 切换自动旋转
 | 
				
			||||||
 | 
					  toggleAutoRotate() {
 | 
				
			||||||
 | 
					    if (this.mainApp.appCamera && this.mainApp.appCamera.object) {
 | 
				
			||||||
 | 
					      const camera = this.mainApp.appCamera.object
 | 
				
			||||||
 | 
					      if (camera.useAutoRotationBehavior) {
 | 
				
			||||||
 | 
					        // 停止自动旋转
 | 
				
			||||||
 | 
					        camera.useAutoRotationBehavior = false
 | 
				
			||||||
 | 
					        console.log('自动旋转已停止')
 | 
				
			||||||
 | 
					        return false
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // 开始自动旋转
 | 
				
			||||||
 | 
					        camera.useAutoRotationBehavior = true
 | 
				
			||||||
 | 
					        if (camera.autoRotationBehavior) {
 | 
				
			||||||
 | 
					          camera.autoRotationBehavior.idleRotationSpeed = 0.5 // 设置旋转速度
 | 
				
			||||||
 | 
					          camera.autoRotationBehavior.idleRotationWaitTime = 1000 // 等待时间
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        console.log('自动旋转已开启')
 | 
				
			||||||
 | 
					        return true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 获取当前自动旋转状态
 | 
				
			||||||
 | 
					  getAutoRotateState() {
 | 
				
			||||||
 | 
					    if (this.mainApp.appCamera && this.mainApp.appCamera.object) {
 | 
				
			||||||
 | 
					      return this.mainApp.appCamera.object.useAutoRotationBehavior || false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 获取GameManager实例
 | 
				
			||||||
 | 
					  getGameManager() {
 | 
				
			||||||
 | 
					    return this.mainApp.gameManager
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 测试播放所有动画
 | 
				
			||||||
 | 
					  testPlayAllAnimations() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 播放动画
 | 
				
			||||||
 | 
					  playAnimation(animationName?: string, loop: boolean = true) {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 在MainEditor.ts中完善dispose方法
 | 
				
			||||||
 | 
					  async dispose() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 释放引擎资源
 | 
				
			||||||
 | 
					    await this.mainApp.dispose();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export default MainEditor
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/script/base/Monobehiver.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					import { MainApp } from 'script/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Monobehiver {
 | 
				
			||||||
 | 
					  mainApp!: MainApp
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    this.mainApp = mainApp
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  Awake() {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/script/base/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export { Monobehiver } from './Monobehiver'
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/script/core.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					export { AppCamera } from '@/script/public/AppCamera'
 | 
				
			||||||
 | 
					export { AppEngin } from '@/script/public/AppEngin'
 | 
				
			||||||
 | 
					export { AppEnv } from '@/script/public/AppEnv'
 | 
				
			||||||
 | 
					export { AppModel } from '@/script/public/AppModel'
 | 
				
			||||||
 | 
					export { AppScene } from '@/script/public/AppScene'
 | 
				
			||||||
 | 
					export { AppLight } from '@/script/public/AppLight'
 | 
				
			||||||
 | 
					export { MainApp } from '@/script/public/MainApp'
 | 
				
			||||||
 | 
					export { AppConfig } from '@/script/public/AppConfig'
 | 
				
			||||||
 | 
					export { AppRay } from '@/script/public/AppRay'
 | 
				
			||||||
 | 
					export { GameManager } from '@/script/public/GameManager'
 | 
				
			||||||
 | 
					export { AppDom } from '@/script/public/AppDom'
 | 
				
			||||||
 | 
					export { AppTween } from '@/script/public/AppTween'
 | 
				
			||||||
 | 
					export { Dimensions } from '@/script/utils/Dimensions'
 | 
				
			||||||
 | 
					export { AppHotspot } from '@/script/public/AppHotspot'
 | 
				
			||||||
 | 
					export { ForMatTime } from '@/script/utils/ForMatTime'
 | 
				
			||||||
 | 
					export { AppFPS } from '@/script/public/AppFPS'
 | 
				
			||||||
 | 
					export { AppGlowLayer } from '@/script/public/AppGlowLayer'
 | 
				
			||||||
 | 
					export { AppCustomization } from '@/script/public/AppCustomization'
 | 
				
			||||||
 | 
					export * from '@/script/utils'
 | 
				
			||||||
							
								
								
									
										110
									
								
								src/script/hotspot/HotSpot.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					import { Point_Pool } from './Point_Pool'
 | 
				
			||||||
 | 
					import { Point } from './Point'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  AbstractMesh,
 | 
				
			||||||
 | 
					  ArcRotateCamera,
 | 
				
			||||||
 | 
					  Engine,
 | 
				
			||||||
 | 
					  Matrix,
 | 
				
			||||||
 | 
					  Scene,
 | 
				
			||||||
 | 
					  Vector3,
 | 
				
			||||||
 | 
					  Texture,
 | 
				
			||||||
 | 
					  StandardMaterial,
 | 
				
			||||||
 | 
					  MeshBuilder,
 | 
				
			||||||
 | 
					  TransformNode
 | 
				
			||||||
 | 
					} from '@babylonjs/core'
 | 
				
			||||||
 | 
					import { HotspotPrams } from './HotspotPrams'
 | 
				
			||||||
 | 
					import { GameManager, MainApp } from '@/script/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HotSpot {
 | 
				
			||||||
 | 
					  _point_Pool!: Point_Pool
 | 
				
			||||||
 | 
					  body!: HTMLElement
 | 
				
			||||||
 | 
					  _camera!: ArcRotateCamera
 | 
				
			||||||
 | 
					  mainApp!: MainApp
 | 
				
			||||||
 | 
					  hotspotTexture!: Texture
 | 
				
			||||||
 | 
					  hotspotMaterial!: StandardMaterial
 | 
				
			||||||
 | 
					  hotspotContainer!: TransformNode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(mainAPP: MainApp) {
 | 
				
			||||||
 | 
					    this.mainApp = mainAPP
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Awake() {
 | 
				
			||||||
 | 
					    this._camera = this.mainApp.appCamera.object
 | 
				
			||||||
 | 
					    this._point_Pool = new Point_Pool()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 创建热点容器
 | 
				
			||||||
 | 
					    this.hotspotContainer = new TransformNode('hotspotContainer', this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 构建并验证资源路径
 | 
				
			||||||
 | 
					    const texturePath = `/src/texture/btn_热点.png`
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // 创建热点纹理
 | 
				
			||||||
 | 
					      this.hotspotTexture = new Texture(texturePath, this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					      this.hotspotTexture.hasAlpha = true
 | 
				
			||||||
 | 
					      this.hotspotTexture.getAlphaFromRGB = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 创建热点材质
 | 
				
			||||||
 | 
					      this.hotspotMaterial = new StandardMaterial('hotspotMaterial', this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					      this.hotspotMaterial.diffuseTexture = this.hotspotTexture
 | 
				
			||||||
 | 
					      this.hotspotMaterial.emissiveTexture = this.hotspotTexture
 | 
				
			||||||
 | 
					      this.hotspotMaterial.opacityTexture = this.hotspotTexture
 | 
				
			||||||
 | 
					      this.hotspotMaterial.disableLighting = true
 | 
				
			||||||
 | 
					      this.hotspotMaterial.backFaceCulling = false
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    } catch (error: any) {
 | 
				
			||||||
 | 
					      // 创建默认材质作为后备
 | 
				
			||||||
 | 
					      this.hotspotMaterial = new StandardMaterial('hotspotMaterial', this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					      this.hotspotMaterial.disableLighting = true
 | 
				
			||||||
 | 
					      this.hotspotMaterial.backFaceCulling = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  vector!: Vector3
 | 
				
			||||||
 | 
					  halfW!: number
 | 
				
			||||||
 | 
					  halfH!: number
 | 
				
			||||||
 | 
					  annotation!: HTMLElement
 | 
				
			||||||
 | 
					  modedl!: AbstractMesh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //创建圆点并且生成事件   类型
 | 
				
			||||||
 | 
					  Point_Event(prams: HotspotPrams) {
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    // 检查纹理是否已加载
 | 
				
			||||||
 | 
					    if (this.hotspotTexture && this.hotspotTexture.isReady()) {
 | 
				
			||||||
 | 
					      // 纹理已准备好,立即创建热点
 | 
				
			||||||
 | 
					      this.createPointPlane(prams)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // 纹理未准备好,等待加载完成
 | 
				
			||||||
 | 
					      if (this.hotspotTexture) {
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					        this.hotspotTexture.onLoadObservable.addOnce(() => {
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					          this.createPointPlane(prams)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        console.error('热点纹理不存在')
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 创建点平面的具体实现
 | 
				
			||||||
 | 
					  createPointPlane(prams: HotspotPrams) {
 | 
				
			||||||
 | 
					    let { position, disposition, onload, onCallBack } = prams
 | 
				
			||||||
 | 
					    let _point = new Point(this.hotspotMaterial, this.hotspotContainer, this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					    _point.init(position, disposition, onload, onCallBack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 将热点添加到热点池中
 | 
				
			||||||
 | 
					    this._point_Pool.Add_point(_point)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Enable_All(visible: boolean) {
 | 
				
			||||||
 | 
					    if (this._point_Pool) {
 | 
				
			||||||
 | 
					      this._point_Pool.Enable_All(visible)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { HotSpot }
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/script/hotspot/HotspotPrams.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					import { Vector3 } from '@babylonjs/core'
 | 
				
			||||||
 | 
					export class HotspotPrams {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    position: Vector3,
 | 
				
			||||||
 | 
					    disposition: Vector3,
 | 
				
			||||||
 | 
					    onload: Function,
 | 
				
			||||||
 | 
					    onCallBack: Function,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.position = position
 | 
				
			||||||
 | 
					    this.disposition = disposition
 | 
				
			||||||
 | 
					    this.onload = onload
 | 
				
			||||||
 | 
					    this.onCallBack = onCallBack
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  position!: Vector3
 | 
				
			||||||
 | 
					  disposition!: Vector3
 | 
				
			||||||
 | 
					  onload!: Function
 | 
				
			||||||
 | 
					  onCallBack!: Function
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										94
									
								
								src/script/hotspot/Point.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					import { 
 | 
				
			||||||
 | 
					  Vector3, 
 | 
				
			||||||
 | 
					  ActionManager, 
 | 
				
			||||||
 | 
					  ExecuteCodeAction, 
 | 
				
			||||||
 | 
					  StandardMaterial, 
 | 
				
			||||||
 | 
					  MeshBuilder, 
 | 
				
			||||||
 | 
					  Mesh,
 | 
				
			||||||
 | 
					  TransformNode,
 | 
				
			||||||
 | 
					  Scene
 | 
				
			||||||
 | 
					} from '@babylonjs/core'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Point {
 | 
				
			||||||
 | 
					  annotation!: HTMLElement
 | 
				
			||||||
 | 
					  showBox!: HTMLElement
 | 
				
			||||||
 | 
					  position!: Vector3
 | 
				
			||||||
 | 
					  disposition!: Vector3
 | 
				
			||||||
 | 
					  onload!: Function
 | 
				
			||||||
 | 
					  onCallBack!: Function
 | 
				
			||||||
 | 
					  offCallBack!: Function
 | 
				
			||||||
 | 
					  isClick!: boolean
 | 
				
			||||||
 | 
					  img!: any
 | 
				
			||||||
 | 
					  plane!: Mesh
 | 
				
			||||||
 | 
					  spriteBehindObject!: boolean
 | 
				
			||||||
 | 
					  hotspotMaterial!: StandardMaterial
 | 
				
			||||||
 | 
					  hotspotContainer!: TransformNode
 | 
				
			||||||
 | 
					  scene!: Scene
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(hotspotMaterial: StandardMaterial, hotspotContainer: TransformNode, scene: Scene) {
 | 
				
			||||||
 | 
					    this.hotspotMaterial = hotspotMaterial
 | 
				
			||||||
 | 
					    this.hotspotContainer = hotspotContainer
 | 
				
			||||||
 | 
					    this.scene = scene
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  init(
 | 
				
			||||||
 | 
					    position: Vector3,
 | 
				
			||||||
 | 
					    disposition: Vector3,
 | 
				
			||||||
 | 
					    onload: Function,
 | 
				
			||||||
 | 
					    onCallBack: Function
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.position = position
 | 
				
			||||||
 | 
					    this.disposition = disposition
 | 
				
			||||||
 | 
					    this.onCallBack = onCallBack
 | 
				
			||||||
 | 
					    this.onload = onload
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.Create_plane()
 | 
				
			||||||
 | 
					    this.setupEvents()
 | 
				
			||||||
 | 
					    //this.Create_annotation(onload, onCallBack)
 | 
				
			||||||
 | 
					    //this.isClick = false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Create_plane() {
 | 
				
			||||||
 | 
					    // 创建一个平面作为热点
 | 
				
			||||||
 | 
					    this.plane = MeshBuilder.CreatePlane(
 | 
				
			||||||
 | 
					      Math.random().toString(36).slice(-6),
 | 
				
			||||||
 | 
					      { 
 | 
				
			||||||
 | 
					        size: 0.14, // 热点大小
 | 
				
			||||||
 | 
					        sideOrientation: Mesh.DOUBLESIDE
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      this.scene
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 设置热点位置
 | 
				
			||||||
 | 
					    this.plane.position.copyFrom(this.position)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 应用材质
 | 
				
			||||||
 | 
					    this.plane.material = this.hotspotMaterial
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 设置为公告牌模式,让热点始终面向摄像机
 | 
				
			||||||
 | 
					    this.plane.billboardMode = Mesh.BILLBOARDMODE_ALL
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 设置父节点为热点容器
 | 
				
			||||||
 | 
					    this.plane.parent = this.hotspotContainer
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 确保热点可见和可交互
 | 
				
			||||||
 | 
					    this.plane.isVisible = true
 | 
				
			||||||
 | 
					    this.plane.isPickable = true
 | 
				
			||||||
 | 
					    this.plane.renderingGroupId=2
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setupEvents() {
 | 
				
			||||||
 | 
					    if (this.plane && this.onCallBack) {
 | 
				
			||||||
 | 
					      this.plane.actionManager = new ActionManager(this.scene)
 | 
				
			||||||
 | 
					      this.plane.actionManager.registerAction(new ExecuteCodeAction(
 | 
				
			||||||
 | 
					        ActionManager.OnPickTrigger,
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
 | 
					          console.log('热点被点击:', this.plane.name)
 | 
				
			||||||
 | 
					          this.onCallBack(this)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/script/hotspot/Point_Pool.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					import { Point } from './Point'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Point_Pool {
 | 
				
			||||||
 | 
					  points
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.points = new Array()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Add_point(point_Class: Point) {
 | 
				
			||||||
 | 
					    this.points.push(point_Class)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Enable_All(visible: boolean) {
 | 
				
			||||||
 | 
					    for (let i = 0, item; (item = this.points[i++]); ) {
 | 
				
			||||||
 | 
					      if (item.plane) {
 | 
				
			||||||
 | 
					        item.plane.isVisible = visible
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								src/script/hotspot/btn_热点.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.1 KiB  | 
							
								
								
									
										3
									
								
								src/script/hotspot/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					export * from './HotSpot'
 | 
				
			||||||
 | 
					export * from './Point'
 | 
				
			||||||
 | 
					export * from './HotspotPrams'
 | 
				
			||||||
							
								
								
									
										157
									
								
								src/script/hotspot/style/point.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,157 @@
 | 
				
			|||||||
 | 
					/* .canvas {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100px;
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.annotation {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					    z-index: 0;
 | 
				
			||||||
 | 
					    margin-left: -10px;
 | 
				
			||||||
 | 
					    margin-top: -10px;
 | 
				
			||||||
 | 
					    width: 30px;
 | 
				
			||||||
 | 
					    height: 30px;
 | 
				
			||||||
 | 
					    border-radius: 10%;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    line-height: 1.2;
 | 
				
			||||||
 | 
					    transition: opacity 0.5s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.line_Right {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 35px;
 | 
				
			||||||
 | 
					    left: 55px;
 | 
				
			||||||
 | 
					    z-index: 0;
 | 
				
			||||||
 | 
					    margin-left: 30px;
 | 
				
			||||||
 | 
					    margin-top: -30px;
 | 
				
			||||||
 | 
					    width: 241px;
 | 
				
			||||||
 | 
					    height: 104px;
 | 
				
			||||||
 | 
					    border-radius: 10%;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    line-height: 1.2;
 | 
				
			||||||
 | 
					    transform-origin: 0 0;
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.line_Left {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 20px;
 | 
				
			||||||
 | 
					    left: -210px;
 | 
				
			||||||
 | 
					    z-index: 0;
 | 
				
			||||||
 | 
					    margin-left: 30px;
 | 
				
			||||||
 | 
					    margin-top: -30px;
 | 
				
			||||||
 | 
					    width: 241px;
 | 
				
			||||||
 | 
					    height: 104px;
 | 
				
			||||||
 | 
					    border-radius: 10%;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    line-height: 1.2;
 | 
				
			||||||
 | 
					    transform-origin: 100% 0;
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ShowBox_left {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 120px;
 | 
				
			||||||
 | 
					    left: -55px;
 | 
				
			||||||
 | 
					    z-index: 1;
 | 
				
			||||||
 | 
					    margin-left: -10px;
 | 
				
			||||||
 | 
					    margin-top: -10px;
 | 
				
			||||||
 | 
					    width: 70px;
 | 
				
			||||||
 | 
					    height: 50px;
 | 
				
			||||||
 | 
					    border-radius: 10%;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    line-height: 50px;
 | 
				
			||||||
 | 
					    transition: opacity 0.5s;
 | 
				
			||||||
 | 
					    background-size: 100%;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ShowBox_right {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: 120px;
 | 
				
			||||||
 | 
					    left: 240px;
 | 
				
			||||||
 | 
					    z-index: 1;
 | 
				
			||||||
 | 
					    margin-left: -10px;
 | 
				
			||||||
 | 
					    margin-top: -10px;
 | 
				
			||||||
 | 
					    width: 70px;
 | 
				
			||||||
 | 
					    height: 50px;
 | 
				
			||||||
 | 
					    border-radius: 10%;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    line-height: 50px;
 | 
				
			||||||
 | 
					    transition: opacity 0.5s;
 | 
				
			||||||
 | 
					    background-size: 100%;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.after {
 | 
				
			||||||
 | 
					    content: attr(Text);
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: -110px;
 | 
				
			||||||
 | 
					    left: 50px;
 | 
				
			||||||
 | 
					    width: 100px;
 | 
				
			||||||
 | 
					    height: 100px;
 | 
				
			||||||
 | 
					    border: 2px solid #fff;
 | 
				
			||||||
 | 
					    border-radius: .5em;
 | 
				
			||||||
 | 
					    font-size: 16px;
 | 
				
			||||||
 | 
					    line-height: 30px;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    background: rgba(0, 0, 0, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#number {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    z-index: -1;
 | 
				
			||||||
 | 
					    opacity: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.linimg {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    background-size: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.pointimg {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    background-size: 100%;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    transition: all 0.2s ease-in;
 | 
				
			||||||
 | 
					    border-radius: 50%;
 | 
				
			||||||
 | 
					    animation: shrink 1s infinite alternate;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes shrink {
 | 
				
			||||||
 | 
					    0% {
 | 
				
			||||||
 | 
					        transform: scale(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    100% {
 | 
				
			||||||
 | 
					        transform: scale(0.8);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.pointimg:hover {
 | 
				
			||||||
 | 
					    transform: scale(1.3);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ChangeShowBox {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: -20px;
 | 
				
			||||||
 | 
					    left: 50px;
 | 
				
			||||||
 | 
					    z-index: 1;
 | 
				
			||||||
 | 
					    margin-left: -20px;
 | 
				
			||||||
 | 
					    margin-top: -60px;
 | 
				
			||||||
 | 
					    width: 150px;
 | 
				
			||||||
 | 
					    height: 120px;
 | 
				
			||||||
 | 
					    border-radius: 10%;
 | 
				
			||||||
 | 
					    font-size: 12px;
 | 
				
			||||||
 | 
					    line-height: 1.2;
 | 
				
			||||||
 | 
					    transition: opacity 0.5s;
 | 
				
			||||||
 | 
					    background-size: 100%;
 | 
				
			||||||
 | 
					} */
 | 
				
			||||||
							
								
								
									
										2
									
								
								src/script/net/config/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					export { ParamsReq, ParamsRes } from './params'
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/script/net/config/params.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					//发送请求   地址和参数
 | 
				
			||||||
 | 
					class ParamsReq {
 | 
				
			||||||
 | 
					  private _url: string = ''
 | 
				
			||||||
 | 
					  private _parms: any
 | 
				
			||||||
 | 
					  //进行封装
 | 
				
			||||||
 | 
					  constructor(url: string, parms: any) {
 | 
				
			||||||
 | 
					    this._url = url
 | 
				
			||||||
 | 
					    this._parms = parms
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public get url(): string {
 | 
				
			||||||
 | 
					    return this._url
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public get parms(): any {
 | 
				
			||||||
 | 
					    return this._parms
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class resParams {
 | 
				
			||||||
 | 
					  data: any = {}
 | 
				
			||||||
 | 
					  message: string = ''
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//接受请求 请求码 和请求回来的参数
 | 
				
			||||||
 | 
					class ParamsRes {
 | 
				
			||||||
 | 
					  public resCode: number
 | 
				
			||||||
 | 
					  public resParams: resParams
 | 
				
			||||||
 | 
					  constructor(resCode: number, resParams: resParams) {
 | 
				
			||||||
 | 
					    this.resCode = resCode
 | 
				
			||||||
 | 
					    this.resParams = resParams
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { ParamsReq, ParamsRes, resParams }
 | 
				
			||||||
							
								
								
									
										4
									
								
								src/script/net/config/ss.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					{"children": 
 | 
				
			||||||
 | 
					[{"id": "1000", "type": "category", "label": "1671172471589", "children": 
 | 
				
			||||||
 | 
					[{"id": "1002", "type": "item", "label": "右肺上叶前段", "modelname": "isPickable_右肺上叶前段", "paramsdata": {"color": "#cc5921", "opacity": "1"}}], 
 | 
				
			||||||
 | 
					"modelname": ""}, {"id": "1001", "type": "category", "label": "1671172471973", "modelname": ""}], "modelurl": "http://192.168.3.151:8084/model/1671171942281-feimoxing.glb"}
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/script/net/netrouter/EditorRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import { Request } from '../request'
 | 
				
			||||||
 | 
					import { ParamsReq, ParamsRes } from 'script/net/config'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//资源请求
 | 
				
			||||||
 | 
					export class EditorRequest {
 | 
				
			||||||
 | 
					  public request: Request = new Request()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Post(url: string, parms: any, resFunc: Function, errFunc?: Function) {
 | 
				
			||||||
 | 
					    const paramsReq = new ParamsReq(url, parms)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.request
 | 
				
			||||||
 | 
					      .post(paramsReq)
 | 
				
			||||||
 | 
					      .then((res) => {
 | 
				
			||||||
 | 
					        resFunc(res.data)
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .catch((err) => {
 | 
				
			||||||
 | 
					        errFunc ? err : ''
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  public production(parms: any, resFunc: Function, errFunc?: Function) {
 | 
				
			||||||
 | 
					    this.Post('cpq/3d/create/order', parms, resFunc, errFunc)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								src/script/net/netrouter/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					import { EditorRequest } from './EditorRequest'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let _editorRequest: EditorRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//编辑器的所有请求
 | 
				
			||||||
 | 
					export function editorRequest() {
 | 
				
			||||||
 | 
					  if (_editorRequest) {
 | 
				
			||||||
 | 
					    return _editorRequest
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    _editorRequest = new EditorRequest()
 | 
				
			||||||
 | 
					    return _editorRequest
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/script/net/request.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import axios from 'axios'
 | 
				
			||||||
 | 
					import { ParamsReq } from 'script/net/config'
 | 
				
			||||||
 | 
					import { AxiosInstance } from 'axios'
 | 
				
			||||||
 | 
					//https://script.jiaweijia.cn/script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Request {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  service: AxiosInstance
 | 
				
			||||||
 | 
					  // import.meta.env.VITE_BASE_URL
 | 
				
			||||||
 | 
					  //'https://script.jiaweijia.cn/script'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    console.log(import.meta.env.VITE_BASE_URL);
 | 
				
			||||||
 | 
					    this.service = axios.create({
 | 
				
			||||||
 | 
					      baseURL: import.meta.env.VITE_BASE_URL,
 | 
				
			||||||
 | 
					      timeout: 15000,
 | 
				
			||||||
 | 
					      headers: {
 | 
				
			||||||
 | 
					        // 这里可设置所有的请求头
 | 
				
			||||||
 | 
					        //'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' //该项建议设置 如果未 POST请求时 数据未做处理时会出现错误,最理想的解决方法就是 直接设置该项
 | 
				
			||||||
 | 
					        //"Content-Type": "multipart/form-data"
 | 
				
			||||||
 | 
					          "Content-Type": "application/json"
 | 
				
			||||||
 | 
					        //'Cache-Control': 'no-cache'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  get(url: string) {
 | 
				
			||||||
 | 
					    return this.service.get(url)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  post(data: ParamsReq) {
 | 
				
			||||||
 | 
					    console.log(data.url);
 | 
				
			||||||
 | 
					    return this.service.post(data.url, data.parms)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  del(url: string) {
 | 
				
			||||||
 | 
					    return this.service.delete(url)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  put(data: ParamsReq) {
 | 
				
			||||||
 | 
					    return this.service.put(data.url, data.parms)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										84
									
								
								src/script/public/AppCamera.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					import { ArcRotateCamera, Tools, Vector3, UniversalCamera } from "@babylonjs/core";
 | 
				
			||||||
 | 
					import { MainApp } from "@/script/public/MainApp";
 | 
				
			||||||
 | 
					import { Monobehiver } from "../base";
 | 
				
			||||||
 | 
					class AppCamera extends Monobehiver {
 | 
				
			||||||
 | 
					  object!: ArcRotateCamera;
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async Awake() {
 | 
				
			||||||
 | 
					    let scene = this.mainApp.appScene.object;
 | 
				
			||||||
 | 
					    this.object = new ArcRotateCamera(
 | 
				
			||||||
 | 
					      'Camera',
 | 
				
			||||||
 | 
					      Tools.ToRadians(0),   // 水平角度:0度(从正前方看)
 | 
				
			||||||
 | 
					      Tools.ToRadians(30),  // 垂直角度:30度(稍微俯视)
 | 
				
			||||||
 | 
					      0,                    // 距离目标的半径:5
 | 
				
			||||||
 | 
					      new Vector3(0.0, 0, 0.0),
 | 
				
			||||||
 | 
					      scene
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.object.fov = 0.8; // 设置焦距
 | 
				
			||||||
 | 
					    this.object.minZ = 0.001; // 进一步减小近裁剪面距离,防止模型被裁剪
 | 
				
			||||||
 | 
					    this.object.maxZ = 10000;
 | 
				
			||||||
 | 
					    this.object.pinchPrecision = 100;
 | 
				
			||||||
 | 
					    this.object.wheelPrecision = 60;
 | 
				
			||||||
 | 
					    this.object.wheelDeltaPercentage = 0.01;
 | 
				
			||||||
 | 
					    this.object.upperRadiusLimit = 20; // 设置最大距离限制
 | 
				
			||||||
 | 
					    this.object.lowerRadiusLimit = 1.0; // 调整最小距离限制,避免过近导致的问题
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.object.panningInertia = 0.9;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 禁用右键拖拽平移功能
 | 
				
			||||||
 | 
					    this.object.panningSensibility = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.object.attachControl(this.mainApp.canvas, true);
 | 
				
			||||||
 | 
					    // 设置相机目标
 | 
				
			||||||
 | 
					    this.setTarget(0, 0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 对于ArcRotateCamera,不要直接设置position,而是调整角度和半径
 | 
				
			||||||
 | 
					    // this.object.position.set(1, 0, 20) // 删除这行,因为它不起作用
 | 
				
			||||||
 | 
					    this.object.position = new Vector3(0, 0, 2)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setTarget(x: number, y: number, z: number) {
 | 
				
			||||||
 | 
					    this.object.target = new Vector3(x, y, z);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 设置相机位置(对于ArcRotateCamera,通过调整角度和半径)
 | 
				
			||||||
 | 
					  setCameraPosition(alpha: number, beta: number, radius: number) {
 | 
				
			||||||
 | 
					    this.object.alpha = Tools.ToRadians(alpha);
 | 
				
			||||||
 | 
					    this.object.beta = Tools.ToRadians(beta);
 | 
				
			||||||
 | 
					    this.object.radius = radius;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 获取当前相机位置信息
 | 
				
			||||||
 | 
					  getCameraInfo() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      alpha: Tools.ToDegrees(this.object.alpha),
 | 
				
			||||||
 | 
					      beta: Tools.ToDegrees(this.object.beta),
 | 
				
			||||||
 | 
					      radius: this.object.radius,
 | 
				
			||||||
 | 
					      target: this.object.target
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clean() {
 | 
				
			||||||
 | 
					    if (this.object) {
 | 
				
			||||||
 | 
					      this.object.detachControl();
 | 
				
			||||||
 | 
					      this.object.dispose();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  update() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AppCamera };
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/script/public/AppConfig.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					export class AppConfig {
 | 
				
			||||||
 | 
					  static [key: string]: any;
 | 
				
			||||||
 | 
					  static modelUrlList: string[];
 | 
				
			||||||
 | 
					  static animationUrlList: string[];
 | 
				
			||||||
 | 
					  static success: Function;
 | 
				
			||||||
 | 
					  static error: Function;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AppParams {
 | 
				
			||||||
 | 
					  model!: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										534
									
								
								src/script/public/AppCustomization.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,534 @@
 | 
				
			|||||||
 | 
					import { MainApp } from '@/script/core'
 | 
				
			||||||
 | 
					import { Monobehiver } from '@/script/base'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  DynamicTexture,
 | 
				
			||||||
 | 
					  NodeMaterial,
 | 
				
			||||||
 | 
					  PBRMaterial,
 | 
				
			||||||
 | 
					  AssetsManager,
 | 
				
			||||||
 | 
					  Color3,
 | 
				
			||||||
 | 
					  Texture,
 | 
				
			||||||
 | 
					} from '@babylonjs/core'
 | 
				
			||||||
 | 
					import { useCustomizationStore } from '@/stores/customization'
 | 
				
			||||||
 | 
					import type { CustomizationLayer } from '@/stores/customization'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppCustomization extends Monobehiver {
 | 
				
			||||||
 | 
					  // 纹理尺寸
 | 
				
			||||||
 | 
					  private readonly imprintWidth = 683
 | 
				
			||||||
 | 
					  private readonly imprintHeight = 2048
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 动态纹理
 | 
				
			||||||
 | 
					  imprintTexture!: DynamicTexture
 | 
				
			||||||
 | 
					  imprintContext!: CanvasRenderingContext2D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 材质(可以是NodeMaterial或PBRMaterial)
 | 
				
			||||||
 | 
					  decalMaterial!: NodeMaterial | PBRMaterial | null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 资源管理器
 | 
				
			||||||
 | 
					  assetsManager!: AssetsManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 图形资源
 | 
				
			||||||
 | 
					  graphics: Array<{
 | 
				
			||||||
 | 
					    image: HTMLImageElement
 | 
				
			||||||
 | 
					    ratio: number
 | 
				
			||||||
 | 
					    size: number
 | 
				
			||||||
 | 
					    name: string
 | 
				
			||||||
 | 
					    url: string
 | 
				
			||||||
 | 
					  }> = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 颜色列表
 | 
				
			||||||
 | 
					  colors: string[] = [
 | 
				
			||||||
 | 
					    'Azure', 'Black', 'DarkGray', 'DimGray', 'Red', 'FireBrick',
 | 
				
			||||||
 | 
					    'DeepPink', 'OrangeRed', 'Orange', 'Gold', 'Yellow', 'RoyalBlue',
 | 
				
			||||||
 | 
					    'CornflowerBlue', 'DeepSkyBlue', 'DarkGreen', 'ForestGreen',
 | 
				
			||||||
 | 
					    'GreenYellow', 'RebeccaPurple', 'DarkViolet', 'MediumPurple'
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Store实例
 | 
				
			||||||
 | 
					  private customizationStore = useCustomizationStore()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 是否正在更新纹理(防止频繁更新)
 | 
				
			||||||
 | 
					  private isUpdatingTexture = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 保存原始纹理,用于混合
 | 
				
			||||||
 | 
					  private originalTextures: Map<string, Texture> = new Map()
 | 
				
			||||||
 | 
					  // 混合后的纹理
 | 
				
			||||||
 | 
					  private blendedTexture: DynamicTexture | null = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async Awake() {
 | 
				
			||||||
 | 
					    // 初始化动态纹理
 | 
				
			||||||
 | 
					    this.initImprintTexture()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 初始化资源管理器
 | 
				
			||||||
 | 
					    this.assetsManager = new AssetsManager(this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 监听store变化
 | 
				
			||||||
 | 
					    this.watchStoreChanges()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log('✅ 定制化系统初始化完成')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 初始化动态纹理
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private initImprintTexture() {
 | 
				
			||||||
 | 
					    this.imprintTexture = new DynamicTexture(
 | 
				
			||||||
 | 
					      'decalTex',
 | 
				
			||||||
 | 
					      { width: this.imprintWidth, height: this.imprintHeight },
 | 
				
			||||||
 | 
					      this.mainApp.appScene.object
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    this.imprintContext = this.imprintTexture.getContext() as CanvasRenderingContext2D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 设置默认背景色
 | 
				
			||||||
 | 
					    this.imprintContext.fillStyle = '#00000000'
 | 
				
			||||||
 | 
					    this.imprintContext.fillRect(0, 0, this.imprintWidth, this.imprintHeight)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 监听store变化并更新纹理
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private watchStoreChanges() {
 | 
				
			||||||
 | 
					    // 监听纹理更新触发标记的变化
 | 
				
			||||||
 | 
					    const unwatch = this.customizationStore.$subscribe((mutation, state) => {
 | 
				
			||||||
 | 
					      // 监听所有customization store的变化
 | 
				
			||||||
 | 
					      // 使用防抖,避免频繁更新
 | 
				
			||||||
 | 
					      if (this.updateTimer) {
 | 
				
			||||||
 | 
					        clearTimeout(this.updateTimer)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.updateTimer = setTimeout(() => {
 | 
				
			||||||
 | 
					        if (!this.isUpdatingTexture) {
 | 
				
			||||||
 | 
					          this.updateImprintTexture()
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          // 更新材质中的纹理引用
 | 
				
			||||||
 | 
					          if (this.decalMaterial) {
 | 
				
			||||||
 | 
					            // 如果是PBR材质,直接更新纹理
 | 
				
			||||||
 | 
					            if (this.decalMaterial instanceof PBRMaterial) {
 | 
				
			||||||
 | 
					              // PBR材质会自动使用更新的纹理引用
 | 
				
			||||||
 | 
					              const scene = this.decalMaterial.getScene()
 | 
				
			||||||
 | 
					              if (scene) {
 | 
				
			||||||
 | 
					                scene.markAllMaterialsAsDirty(0) // 0表示所有材质
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // 如果是NodeMaterial,纹理会自动更新
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }, 100)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 保存unwatch以便后续清理
 | 
				
			||||||
 | 
					    this.unwatchStore = unwatch
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private updateTimer: any = null
 | 
				
			||||||
 | 
					  private unwatchStore: any = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 更新印记纹理
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  updateImprintTexture() {
 | 
				
			||||||
 | 
					    if (this.isUpdatingTexture) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.isUpdatingTexture = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // 清空画布
 | 
				
			||||||
 | 
					      this.imprintContext.clearRect(0, 0, this.imprintWidth, this.imprintHeight)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const layers = this.customizationStore.layers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 遍历所有层并绘制
 | 
				
			||||||
 | 
					      for (const layer of layers) {
 | 
				
			||||||
 | 
					        if (!layer.visible || (!layer.graphic && !layer.rectangle)) {
 | 
				
			||||||
 | 
					          continue
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.imprintContext.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (layer.graphic) {
 | 
				
			||||||
 | 
					          // 绘制图形
 | 
				
			||||||
 | 
					          this.drawGraphicLayer(layer)
 | 
				
			||||||
 | 
					        } else if (layer.rectangle) {
 | 
				
			||||||
 | 
					          // 绘制矩形
 | 
				
			||||||
 | 
					          this.drawRectangleLayer(layer)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.imprintContext.restore()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 更新纹理
 | 
				
			||||||
 | 
					      this.imprintTexture.update()
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('更新纹理失败:', error)
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      this.isUpdatingTexture = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 绘制图形层
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private drawGraphicLayer(layer: CustomizationLayer) {
 | 
				
			||||||
 | 
					    if (!layer.graphic) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 计算位置偏移
 | 
				
			||||||
 | 
					    const graphicU = layer.horizontal * this.imprintWidth * 0.5
 | 
				
			||||||
 | 
					    const graphicV = layer.vertical * this.imprintHeight * -0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 移动到中心位置
 | 
				
			||||||
 | 
					    this.imprintContext.translate(
 | 
				
			||||||
 | 
					      this.imprintWidth * 0.5 + graphicU,
 | 
				
			||||||
 | 
					      this.imprintHeight * 0.5 + graphicV
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 计算绘制位置和尺寸
 | 
				
			||||||
 | 
					    const left = layer.size * layer.ratio * -0.5
 | 
				
			||||||
 | 
					    const top = layer.size * -0.5
 | 
				
			||||||
 | 
					    const width = layer.ratio * layer.size
 | 
				
			||||||
 | 
					    const height = layer.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 旋转
 | 
				
			||||||
 | 
					    this.imprintContext.rotate(layer.rotation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 绘制图像
 | 
				
			||||||
 | 
					    this.imprintContext.drawImage(layer.graphic, left, top, width, height)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 绘制矩形层
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private drawRectangleLayer(layer: CustomizationLayer) {
 | 
				
			||||||
 | 
					    // 计算矩形位置和尺寸
 | 
				
			||||||
 | 
					    const left = (1 - layer.width) * 0.5 * this.imprintWidth
 | 
				
			||||||
 | 
					    const top = (1 - layer.height) * 0.5 * this.imprintHeight
 | 
				
			||||||
 | 
					    const width = layer.width * this.imprintWidth
 | 
				
			||||||
 | 
					    const height = layer.height * this.imprintHeight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 计算偏移
 | 
				
			||||||
 | 
					    const offsetU = layer.horizontal * this.imprintWidth * 0.5
 | 
				
			||||||
 | 
					    const offsetV = layer.vertical * this.imprintHeight * -0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 计算矩形中心
 | 
				
			||||||
 | 
					    const rectangleU = width * 0.5 + left
 | 
				
			||||||
 | 
					    const rectangleV = height * 0.5 + top
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 移动到中心并应用偏移
 | 
				
			||||||
 | 
					    this.imprintContext.translate(rectangleU + offsetU, rectangleV + offsetV)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 旋转
 | 
				
			||||||
 | 
					    this.imprintContext.rotate(layer.rotation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 设置填充颜色
 | 
				
			||||||
 | 
					    this.imprintContext.fillStyle = layer.fillStyle || '#00000000'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 绘制矩形
 | 
				
			||||||
 | 
					    this.imprintContext.fillRect(-width * 0.5, -height * 0.5, width, height)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 加载资源(图形、图标等)
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async loadAssets(serverUrl: string = '') {
 | 
				
			||||||
 | 
					    return new Promise<void>((resolve, reject) => {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        // 加载图形资源
 | 
				
			||||||
 | 
					        this.loadGraphicAssets(serverUrl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 加载图标资源
 | 
				
			||||||
 | 
					        this.loadIconAssets(serverUrl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 开始加载
 | 
				
			||||||
 | 
					        this.assetsManager.load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 错误处理
 | 
				
			||||||
 | 
					        this.assetsManager.onTaskErrorObservable.add((task) => {
 | 
				
			||||||
 | 
					          console.error('资源加载失败:', task.name, task.errorObject)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 完成回调
 | 
				
			||||||
 | 
					        this.assetsManager.onFinish = (tasks) => {
 | 
				
			||||||
 | 
					          console.log('✅ 所有资源加载完成')
 | 
				
			||||||
 | 
					          resolve()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        reject(error)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 加载图形资源
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private loadGraphicAssets(serverUrl: string) {
 | 
				
			||||||
 | 
					    // 示例图形资源列表,可以根据实际需求修改
 | 
				
			||||||
 | 
					    const graphicNames = [
 | 
				
			||||||
 | 
					      'Babylon Logo',
 | 
				
			||||||
 | 
					      'Flame Skull',
 | 
				
			||||||
 | 
					      'Blue Character',
 | 
				
			||||||
 | 
					      'Orange Character',
 | 
				
			||||||
 | 
					      'Purple Character',
 | 
				
			||||||
 | 
					      'Pony',
 | 
				
			||||||
 | 
					      'Rays',
 | 
				
			||||||
 | 
					      'Waves'
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    graphicNames.forEach((name) => {
 | 
				
			||||||
 | 
					      const task = this.assetsManager.addImageTask(
 | 
				
			||||||
 | 
					        `load image_${name}`,
 | 
				
			||||||
 | 
					        `${serverUrl}assets/textures/${name.toLowerCase().replace(/\s/g, '')}.png`
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      task.onSuccess = (task) => {
 | 
				
			||||||
 | 
					        if (task.image) {
 | 
				
			||||||
 | 
					          const graphic = {
 | 
				
			||||||
 | 
					            image: task.image,
 | 
				
			||||||
 | 
					            ratio: task.image.width / task.image.height,
 | 
				
			||||||
 | 
					            size: task.image.width,
 | 
				
			||||||
 | 
					            name: name,
 | 
				
			||||||
 | 
					            url: task.image.src || ''
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          this.graphics.push(graphic)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 添加到store
 | 
				
			||||||
 | 
					          this.customizationStore.addGraphicAsset({
 | 
				
			||||||
 | 
					            name: graphic.name,
 | 
				
			||||||
 | 
					            image: graphic.image,
 | 
				
			||||||
 | 
					            ratio: graphic.ratio,
 | 
				
			||||||
 | 
					            size: graphic.size,
 | 
				
			||||||
 | 
					            url: graphic.url
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 加载图标资源
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private loadIconAssets(serverUrl: string) {
 | 
				
			||||||
 | 
					    // 这里可以加载图标资源,如上下箭头、取消图标等
 | 
				
			||||||
 | 
					    // 暂时留空,后续可根据需要添加
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 创建贴花材质(简化版,直接使用纹理而不需要着色器JSON)
 | 
				
			||||||
 | 
					   * 如果需要完整功能,可以使用带shaderJsonPath的版本
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  createSimpleDecalMaterial(mesh: any) {
 | 
				
			||||||
 | 
					    if (!mesh || !mesh.material) {
 | 
				
			||||||
 | 
					      console.warn('网格或材质不存在')
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // 保存原始材质信息
 | 
				
			||||||
 | 
					      const originalMaterial = mesh.material
 | 
				
			||||||
 | 
					      const baseColorTex = (originalMaterial as any).albedoTexture || (originalMaterial as any).baseColor?.texture
 | 
				
			||||||
 | 
					      const ormTex = (originalMaterial as any).metallicTexture || (originalMaterial as any).metallicRoughnessTexture
 | 
				
			||||||
 | 
					      const normalTex = (originalMaterial as any).bumpTexture || (originalMaterial as any).normalTexture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 如果原始材质是PBR材质,直接更新纹理
 | 
				
			||||||
 | 
					      if (originalMaterial.getClassName && originalMaterial.getClassName().includes('PBR')) {
 | 
				
			||||||
 | 
					        // 创建一个新的PBR材质来混合贴花
 | 
				
			||||||
 | 
					        const newMaterial = new PBRMaterial('decalMaterial_' + mesh.name, this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 复制原始纹理
 | 
				
			||||||
 | 
					        if (baseColorTex) {
 | 
				
			||||||
 | 
					          newMaterial.albedoTexture = baseColorTex
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (ormTex) {
 | 
				
			||||||
 | 
					          newMaterial.metallicTexture = ormTex
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (normalTex) {
 | 
				
			||||||
 | 
					          newMaterial.bumpTexture = normalTex
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 将贴花纹理与原始纹理混合
 | 
				
			||||||
 | 
					        if (!baseColorTex) {
 | 
				
			||||||
 | 
					          // 如果原始材质没有纹理,直接用贴花纹理
 | 
				
			||||||
 | 
					          newMaterial.albedoTexture = this.imprintTexture
 | 
				
			||||||
 | 
					          console.log('📝 使用贴花纹理作为albedo纹理(无原始纹理)')
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          // 保存原始纹理
 | 
				
			||||||
 | 
					          this.originalTextures.set(mesh.name, baseColorTex)
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          // 直接保留原始纹理作为albedo
 | 
				
			||||||
 | 
					          // 注意:PBRMaterial 不支持直接混合两个albedo纹理
 | 
				
			||||||
 | 
					          // 这里我们保留原始纹理,贴花效果需要后续通过NodeMaterial或自定义着色器实现
 | 
				
			||||||
 | 
					          newMaterial.albedoTexture = baseColorTex
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          // 更新贴花纹理内容(虽然当前不直接使用,但保持更新以备将来使用)
 | 
				
			||||||
 | 
					          this.updateImprintTexture()
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          console.log('✅ 保留原始albedo纹理,贴花纹理已准备(当前未直接混合)')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 更新纹理
 | 
				
			||||||
 | 
					        this.updateImprintTexture()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 应用新材质
 | 
				
			||||||
 | 
					        if (originalMaterial !== newMaterial) {
 | 
				
			||||||
 | 
					          originalMaterial.dispose()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        mesh.material = newMaterial
 | 
				
			||||||
 | 
					        this.decalMaterial = newMaterial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 强制刷新材质
 | 
				
			||||||
 | 
					        newMaterial.markDirty()
 | 
				
			||||||
 | 
					        const scene = this.mainApp.appScene.object
 | 
				
			||||||
 | 
					        scene.markAllMaterialsAsDirty(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.log('✅ 贴花材质已应用到网格:', mesh.name)
 | 
				
			||||||
 | 
					        console.log('   基础纹理:', baseColorTex ? '有' : '无')
 | 
				
			||||||
 | 
					        console.log('   贴花纹理:', this.imprintTexture ? '已设置' : '未设置')
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('创建贴花材质失败:', error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 创建贴花材质(完整版,使用着色器JSON)
 | 
				
			||||||
 | 
					   * @param baseMaterial 基础材质
 | 
				
			||||||
 | 
					   * @param shaderJsonPath 着色器JSON路径
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async createDecalMaterial(
 | 
				
			||||||
 | 
					    baseMaterial: any,
 | 
				
			||||||
 | 
					    shaderJsonPath: string
 | 
				
			||||||
 | 
					  ): Promise<NodeMaterial> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // 加载着色器JSON
 | 
				
			||||||
 | 
					      const shaderTask = this.assetsManager.addTextFileTask(
 | 
				
			||||||
 | 
					        'load decal shader',
 | 
				
			||||||
 | 
					        shaderJsonPath
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					        shaderTask.onSuccess = () => {
 | 
				
			||||||
 | 
					          try {
 | 
				
			||||||
 | 
					            const shaderJson = JSON.parse(shaderTask.text)
 | 
				
			||||||
 | 
					            const material = NodeMaterial.Parse(shaderJson, this.mainApp.appScene.object)
 | 
				
			||||||
 | 
					            material.name = 'decalMaterial'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 设置纹理
 | 
				
			||||||
 | 
					            const decalTexBlock = material.getBlockByName('decalTex')
 | 
				
			||||||
 | 
					            if (decalTexBlock && (decalTexBlock as any).texture !== undefined) {
 | 
				
			||||||
 | 
					              (decalTexBlock as any).texture = this.imprintTexture
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 设置基础纹理
 | 
				
			||||||
 | 
					            const baseColorBlock = material.getBlockByName('baseColorTex')
 | 
				
			||||||
 | 
					            if (baseColorBlock && baseMaterial && (baseColorBlock as any).texture !== undefined) {
 | 
				
			||||||
 | 
					              (baseColorBlock as any).texture = (baseMaterial as any).albedoTexture || (baseMaterial as any).baseColor?.texture
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 更新纹理
 | 
				
			||||||
 | 
					            this.updateImprintTexture()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.decalMaterial = material
 | 
				
			||||||
 | 
					            resolve(material)
 | 
				
			||||||
 | 
					          } catch (error) {
 | 
				
			||||||
 | 
					            reject(error)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        shaderTask.onError = () => {
 | 
				
			||||||
 | 
					          reject(new Error('加载着色器失败'))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      throw new Error(`创建贴花材质失败: ${error}`)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 应用材质到网格
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  applyDecalMaterialToMesh(mesh: any) {
 | 
				
			||||||
 | 
					    if (!this.decalMaterial || !mesh) {
 | 
				
			||||||
 | 
					      console.warn('材质或网格未准备好')
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果网格有材质,先释放
 | 
				
			||||||
 | 
					    if (mesh.material && mesh.material !== this.decalMaterial) {
 | 
				
			||||||
 | 
					      mesh.material.dispose()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 应用新材质
 | 
				
			||||||
 | 
					    mesh.material = this.decalMaterial
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 自动为所有加载的网格应用贴花材质
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  applyToAllMeshes() {
 | 
				
			||||||
 | 
					    const meshes = this.mainApp.appScene.object.meshes
 | 
				
			||||||
 | 
					    console.log('🔍 找到网格数量:', meshes.length)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 打印所有网格信息
 | 
				
			||||||
 | 
					    meshes.forEach((mesh: any) => {
 | 
				
			||||||
 | 
					      console.log('网格:', mesh.name, '材质:', mesh.material?.name || '无')
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 找到主要的网格(通常是有材质的网格)
 | 
				
			||||||
 | 
					    const mainMeshes = meshes.filter((mesh: any) => {
 | 
				
			||||||
 | 
					      return mesh.material && !mesh.name.includes('__root__') && mesh.name
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (mainMeshes.length === 0) {
 | 
				
			||||||
 | 
					      console.warn('⚠️ 未找到有材质的网格,尝试所有网格...')
 | 
				
			||||||
 | 
					      // 如果没有找到,尝试所有有名称的网格
 | 
				
			||||||
 | 
					      const allNamedMeshes = meshes.filter((mesh: any) => mesh.name && !mesh.name.includes('__root__'))
 | 
				
			||||||
 | 
					      allNamedMeshes.forEach((mesh: any) => {
 | 
				
			||||||
 | 
					        console.log('尝试为网格应用材质:', mesh.name)
 | 
				
			||||||
 | 
					        this.createSimpleDecalMaterial(mesh)
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 为主要网格应用贴花材质
 | 
				
			||||||
 | 
					    mainMeshes.forEach((mesh: any) => {
 | 
				
			||||||
 | 
					      console.log('✅ 为网格应用贴花材质:', mesh.name)
 | 
				
			||||||
 | 
					      this.createSimpleDecalMaterial(mesh)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log('✅ 贴花材质应用完成')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 获取纹理
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  getImprintTexture(): DynamicTexture {
 | 
				
			||||||
 | 
					    return this.imprintTexture
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 清理资源
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  dispose() {
 | 
				
			||||||
 | 
					    if (this.updateTimer) {
 | 
				
			||||||
 | 
					      clearTimeout(this.updateTimer)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.unwatchStore) {
 | 
				
			||||||
 | 
					      this.unwatchStore()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.imprintTexture) {
 | 
				
			||||||
 | 
					      this.imprintTexture.dispose()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.decalMaterial) {
 | 
				
			||||||
 | 
					      this.decalMaterial.dispose()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // AssetsManager没有dispose方法,只需要清理资源即可
 | 
				
			||||||
 | 
					    if (this.assetsManager) {
 | 
				
			||||||
 | 
					      // 清理所有任务(AssetsManager会自动管理,无需手动清理)
 | 
				
			||||||
 | 
					      // 如果需要,可以重置资源管理器
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AppCustomization }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										85
									
								
								src/script/public/AppDom.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					declare class LoadText {
 | 
				
			||||||
 | 
					  [key:string]:any;
 | 
				
			||||||
 | 
					  dom: HTMLElement | undefined
 | 
				
			||||||
 | 
					  mode: boolean
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AppDom {
 | 
				
			||||||
 | 
					  private _renderDom!: HTMLCanvasElement
 | 
				
			||||||
 | 
					  private _body!: HTMLBodyElement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private _load3D!: HTMLElement
 | 
				
			||||||
 | 
					  private _ui!: HTMLElement
 | 
				
			||||||
 | 
					  private _panTarget!: HTMLElement
 | 
				
			||||||
 | 
					  private _fps!: HTMLElement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private _loadtext: LoadText = {
 | 
				
			||||||
 | 
					    dom: undefined,
 | 
				
			||||||
 | 
					    mode: false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  private _loadsliderbar: LoadText = {
 | 
				
			||||||
 | 
					    dom: undefined,
 | 
				
			||||||
 | 
					    value:0,
 | 
				
			||||||
 | 
					    mode: false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  private _hotspotContainer!: HTMLElement 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public get renderDom(): HTMLCanvasElement {
 | 
				
			||||||
 | 
					    return this._renderDom && this._renderDom
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public get body(): HTMLBodyElement {
 | 
				
			||||||
 | 
					    return this._body && this._body
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public get loadtext(): any {
 | 
				
			||||||
 | 
					    return this._loadtext && this._loadtext
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public get loadsliderbar(): any {
 | 
				
			||||||
 | 
					    return this._loadsliderbar && this._loadsliderbar
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public get hotspotContainer(): HTMLElement {
 | 
				
			||||||
 | 
					    return this._hotspotContainer && this._hotspotContainer
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public get load3D(): HTMLElement {
 | 
				
			||||||
 | 
					    return this._load3D && this._load3D
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public get ui(): HTMLElement {
 | 
				
			||||||
 | 
					    return this._ui && this._ui
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public get panTarget(): any {
 | 
				
			||||||
 | 
					    return this._panTarget && this._panTarget
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public get fps(): HTMLElement {
 | 
				
			||||||
 | 
					    return this._fps && this._fps
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Awake(): void {
 | 
				
			||||||
 | 
					    this._renderDom = this.getElment<HTMLCanvasElement>('#renderDom')
 | 
				
			||||||
 | 
					    this._body = this.getElment<HTMLBodyElement>('body')
 | 
				
			||||||
 | 
					    this._loadtext.dom = this.getElment<HTMLElement>('#loadtext')
 | 
				
			||||||
 | 
					    this._loadsliderbar.dom = this.getElment<HTMLElement>('.loadslider-bar')
 | 
				
			||||||
 | 
					    this._load3D=this.getElment<HTMLElement>('.load3D')
 | 
				
			||||||
 | 
					    this._panTarget=this.getElment<HTMLElement>('.pan-target')
 | 
				
			||||||
 | 
					    this._ui=this.getElment<HTMLElement>('.ui')
 | 
				
			||||||
 | 
					    this._hotspotContainer=this.createElment<HTMLElement>('div')
 | 
				
			||||||
 | 
					    this._fps=this.getElment<HTMLElement>('.fps')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getElment<T extends HTMLElement>(t: string) {
 | 
				
			||||||
 | 
					    return document.querySelector(t) as T
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  createElment<T extends HTMLElement>(t: string) {
 | 
				
			||||||
 | 
					    const element = document.createElement(t)
 | 
				
			||||||
 | 
					    element.className='hotspot-container'
 | 
				
			||||||
 | 
					    document.querySelector('.app-view')?.appendChild(element)
 | 
				
			||||||
 | 
					    return element as T
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										69
									
								
								src/script/public/AppEngin.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					import { Database, Engine } from '@babylonjs/core'
 | 
				
			||||||
 | 
					import { MainApp } from '@/script/core'
 | 
				
			||||||
 | 
					import { Monobehiver } from '@/script/base'
 | 
				
			||||||
 | 
					class AppEngin extends Monobehiver {
 | 
				
			||||||
 | 
					  object!: Engine
 | 
				
			||||||
 | 
					  canvas!: HTMLCanvasElement
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  Awake() {
 | 
				
			||||||
 | 
					    this.createEngin()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  createEngin() {
 | 
				
			||||||
 | 
					    //创建画布引擎
 | 
				
			||||||
 | 
					    this.canvas = this.mainApp.appDom.renderDom
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.object = new Engine(this.canvas, true, {
 | 
				
			||||||
 | 
					      preserveDrawingBuffer: false,
 | 
				
			||||||
 | 
					      stencil: true,
 | 
				
			||||||
 | 
					      alpha: true // 关键点
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    this.object.setSize(window.innerWidth, window.innerHeight)
 | 
				
			||||||
 | 
					    this.object.enableOfflineSupport = true //取消缓存
 | 
				
			||||||
 | 
					    this.object.setHardwareScalingLevel(1) // 移动设备使用更高的缩放级别
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.object.displayLoadingUI()
 | 
				
			||||||
 | 
					    this.object.hideLoadingUI()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 防止过多重复触发resize事件
 | 
				
			||||||
 | 
					    let resizeTimeout: number | null = null;
 | 
				
			||||||
 | 
					    window.addEventListener('resize', () => {
 | 
				
			||||||
 | 
					      if (resizeTimeout) {
 | 
				
			||||||
 | 
					        clearTimeout(resizeTimeout);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      resizeTimeout = window.setTimeout(this.handleResize.bind(this), 100);
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 在AppEngin.ts文件中,添加一个方法来获取当前渲染比例
 | 
				
			||||||
 | 
					getRenderedSizeRatio() {
 | 
				
			||||||
 | 
					  const canvas = this.canvas;
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    widthRatio: canvas.width / canvas.clientWidth,
 | 
				
			||||||
 | 
					    heightRatio: canvas.height / canvas.clientHeight
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleResize() {
 | 
				
			||||||
 | 
					    if(this.object) {
 | 
				
			||||||
 | 
					      this.object.setSize(window.innerWidth, window.innerHeight)
 | 
				
			||||||
 | 
					      this.object.resize()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  clean() {
 | 
				
			||||||
 | 
					    this.object && this.object.dispose()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  update() {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AppEngin }
 | 
				
			||||||
							
								
								
									
										102
									
								
								src/script/public/AppEnv.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					  CubeTexture,
 | 
				
			||||||
 | 
					  Mesh,
 | 
				
			||||||
 | 
					  BackgroundMaterial,
 | 
				
			||||||
 | 
					  Texture,
 | 
				
			||||||
 | 
					  Vector3,
 | 
				
			||||||
 | 
					  MeshBuilder,
 | 
				
			||||||
 | 
					  PhotoDome,
 | 
				
			||||||
 | 
					  StandardMaterial,
 | 
				
			||||||
 | 
					  PBRMaterial,
 | 
				
			||||||
 | 
					  Plane,
 | 
				
			||||||
 | 
					  Animation,
 | 
				
			||||||
 | 
					  AnimationGroup,
 | 
				
			||||||
 | 
					} from '@babylonjs/core';
 | 
				
			||||||
 | 
					import { MainApp } from '@/script/public/MainApp';
 | 
				
			||||||
 | 
					import { Monobehiver } from '../base';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppEnv extends Monobehiver {
 | 
				
			||||||
 | 
					  object!: CubeTexture;
 | 
				
			||||||
 | 
					  sphere: Mesh | null = null;
 | 
				
			||||||
 | 
					  ground: Mesh | null = null;
 | 
				
			||||||
 | 
					  isNeishiMode = true;
 | 
				
			||||||
 | 
					  isTransitioning = false; // 添加过渡状态标记
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async Awake() {
 | 
				
			||||||
 | 
					    this.createHDR();
 | 
				
			||||||
 | 
					    //  this.createPanorama_neishi()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createHDR() {
 | 
				
			||||||
 | 
					    const reflectionTexture = CubeTexture.CreateFromPrefilteredData(
 | 
				
			||||||
 | 
					      '/hdr/hdr.env',
 | 
				
			||||||
 | 
					      this.mainApp.appScene.object
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    reflectionTexture.level = 1;
 | 
				
			||||||
 | 
					    reflectionTexture.rotationY = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.mainApp.appScene.object.environmentIntensity = 1.5;
 | 
				
			||||||
 | 
					    this.mainApp.appScene.object.environmentTexture = reflectionTexture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // reflectionTexture.anisotropicFilteringLevel = 1;
 | 
				
			||||||
 | 
					    const box = this.mainApp.appScene.object.createDefaultSkybox(
 | 
				
			||||||
 | 
					      reflectionTexture,
 | 
				
			||||||
 | 
					      true,
 | 
				
			||||||
 | 
					      512,
 | 
				
			||||||
 | 
					      0,
 | 
				
			||||||
 | 
					      true
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    if (box) box.visibility = 0;
 | 
				
			||||||
 | 
					    // 保存环境纹理的引用
 | 
				
			||||||
 | 
					    this.object = reflectionTexture;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //调整hdr亮度
 | 
				
			||||||
 | 
					  changeHDRIntensity(intensity: number) {
 | 
				
			||||||
 | 
					    this.mainApp.appScene.object.environmentIntensity = intensity;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 旋转HDR环境贴图
 | 
				
			||||||
 | 
					  rotateHDR(angle: number) {
 | 
				
			||||||
 | 
					    if (this.object) {
 | 
				
			||||||
 | 
					      this.object.rotationY = angle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 旋转全景图
 | 
				
			||||||
 | 
					  rotatePanorama_neishi(angle: number) {
 | 
				
			||||||
 | 
					    console.log(this.photoDome_neishi);
 | 
				
			||||||
 | 
					    if (this.photoDome_neishi) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // this.photoDome_neishi.mesh.rotation.y = angle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  photoDome_neishi: PhotoDome | null = null;
 | 
				
			||||||
 | 
					  photoDome_yundong: PhotoDome | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clean() {
 | 
				
			||||||
 | 
					    // 如果存在环境纹理,释放它
 | 
				
			||||||
 | 
					    if (this.object) {
 | 
				
			||||||
 | 
					      this.object.dispose();
 | 
				
			||||||
 | 
					      this.object = null as any;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AppEnv };
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/script/public/AppFPS.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import { Monobehiver } from "../base";
 | 
				
			||||||
 | 
					import { MainApp } from "./MainApp";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AppFPS extends Monobehiver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fpsDisplay!: HTMLDivElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					        super(mainApp)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Awake(): void {
 | 
				
			||||||
 | 
						// 创建 FPS 显示元素
 | 
				
			||||||
 | 
					    this.createFpsDisplay();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    createFpsDisplay() {
 | 
				
			||||||
 | 
					        this.fpsDisplay = document.createElement("div");
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.position = "absolute";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.top = "10px";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.left = "10px";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.color = "white";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.background = "rgba(0, 0, 0, 0.5)";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.padding = "5px 10px";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.fontFamily = "Arial, sans-serif";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.borderRadius = "4px";
 | 
				
			||||||
 | 
					        this.fpsDisplay.style.zIndex = "100";
 | 
				
			||||||
 | 
					        this.fpsDisplay.innerText = "FPS: 0";
 | 
				
			||||||
 | 
					        document.body.appendChild(this.fpsDisplay);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public update() {
 | 
				
			||||||
 | 
					        const fps = this.mainApp.appEngin.object.getFps().toFixed(2);
 | 
				
			||||||
 | 
					        this.fpsDisplay.innerText = `FPS: ${fps}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										65
									
								
								src/script/public/AppGlowLayer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					  AbstractMesh,
 | 
				
			||||||
 | 
					  BloomEffect,
 | 
				
			||||||
 | 
					  Color3,
 | 
				
			||||||
 | 
					  GlowLayer,
 | 
				
			||||||
 | 
					  HighlightLayer,
 | 
				
			||||||
 | 
					  LensFlare,
 | 
				
			||||||
 | 
					  LensFlareSystem,
 | 
				
			||||||
 | 
					  Mesh,
 | 
				
			||||||
 | 
					  MeshBuilder,
 | 
				
			||||||
 | 
					  PBRMaterial,
 | 
				
			||||||
 | 
					  PointLight,
 | 
				
			||||||
 | 
					  StandardMaterial,
 | 
				
			||||||
 | 
					  Vector3
 | 
				
			||||||
 | 
					} from '@babylonjs/core'
 | 
				
			||||||
 | 
					import { MainApp } from 'script/core'
 | 
				
			||||||
 | 
					import { Monobehiver } from 'script/base'
 | 
				
			||||||
 | 
					import { GameManager } from './GameManager'
 | 
				
			||||||
 | 
					import { Dictionary } from '../utils'
 | 
				
			||||||
 | 
					export class AppGlowLayer extends Monobehiver {
 | 
				
			||||||
 | 
					  gl!: GlowLayer
 | 
				
			||||||
 | 
					  index: number = 0
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  Awake() {
 | 
				
			||||||
 | 
					   // this.create()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  create() {
 | 
				
			||||||
 | 
					    if (this.gl) return
 | 
				
			||||||
 | 
					    this.gl = new GlowLayer('gl', this.mainApp.appScene.object, {
 | 
				
			||||||
 | 
					      mainTextureSamples: 10,
 | 
				
			||||||
 | 
					      mainTextureRatio: 60,
 | 
				
			||||||
 | 
					      mainTextureFixedSize: 512,
 | 
				
			||||||
 | 
					      blurKernelSize: 50
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  //添加需要发光的网格
 | 
				
			||||||
 | 
					  addMesh(mesh: Mesh) {
 | 
				
			||||||
 | 
					    this.create()
 | 
				
			||||||
 | 
					    console.log(this.gl.removeExcludedMesh);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.gl.removeExcludedMesh(mesh)
 | 
				
			||||||
 | 
					    this.index++
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  removeMesh(mesh: Mesh) {
 | 
				
			||||||
 | 
					    this.gl.addExcludedMesh(mesh)
 | 
				
			||||||
 | 
					    this.index--
 | 
				
			||||||
 | 
					    if (this.index <= 0) {
 | 
				
			||||||
 | 
					      this.clean()
 | 
				
			||||||
 | 
					      ;(this.gl as any) = null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clean() {
 | 
				
			||||||
 | 
					    this.gl.dispose()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  update() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hasModels(): boolean {
 | 
				
			||||||
 | 
					    console.log(this.index);
 | 
				
			||||||
 | 
					    return this.index > 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										145
									
								
								src/script/public/AppHotspot.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					import { Color3, PBRMaterial, Vector3 } from '@babylonjs/core'
 | 
				
			||||||
 | 
					import { Monobehiver } from '../base'
 | 
				
			||||||
 | 
					import { AppConfig } from '@/script/public/AppConfig'
 | 
				
			||||||
 | 
					import { MainApp } from '@/script/public/MainApp'
 | 
				
			||||||
 | 
					import { Dictionary } from '@/script/utils/Dictionary'
 | 
				
			||||||
 | 
					import { Tools } from '@/script/utils/Tools'
 | 
				
			||||||
 | 
					import { HotSpot, HotspotPrams, Point } from '../hotspot'
 | 
				
			||||||
 | 
					// 在任何文件中
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { userSellingPointStore } from '@/stores/zguiy';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AppHotspot extends Monobehiver {
 | 
				
			||||||
 | 
					  hotSpot!: HotSpot
 | 
				
			||||||
 | 
					  sllingPointStore: any
 | 
				
			||||||
 | 
					  //偏移量
 | 
				
			||||||
 | 
					  offset: number = 0.7
 | 
				
			||||||
 | 
					  yundong: boolean = false
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hotspotDic: Dictionary<HotSpot> = new Dictionary()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp)
 | 
				
			||||||
 | 
					    this.sllingPointStore = userSellingPointStore()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Awake() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const currentModelId = this.mainApp.carModelStore.carModel;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 首先隐藏所有其他车型的热点
 | 
				
			||||||
 | 
					    this.hotspotDic.Keys().forEach((key: string) => {
 | 
				
			||||||
 | 
					      if (key !== currentModelId) {
 | 
				
			||||||
 | 
					        const hotspot = this.hotspotDic.Get(key);
 | 
				
			||||||
 | 
					        if (hotspot) {
 | 
				
			||||||
 | 
					          hotspot.Enable_All(false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (this.hotspotDic.Has(currentModelId)) {
 | 
				
			||||||
 | 
					      this.hotSpot = this.hotspotDic.Get(currentModelId)
 | 
				
			||||||
 | 
					      // 显示当前车型的热点
 | 
				
			||||||
 | 
					      this.hotSpot.Enable_All(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      const hotspot = new HotSpot(this.mainApp)
 | 
				
			||||||
 | 
					      hotspot.Awake()
 | 
				
			||||||
 | 
					      this.hotSpot = hotspot;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      this.initHotSpot(AppConfig.HotSpotList)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					      this.hotspotDic.Set(currentModelId, hotspot)
 | 
				
			||||||
 | 
					      // 新创建的热点默认就是显示的
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  initHotSpot(hotSpotList: Array<any>) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hotSpotList.forEach((hotspot: any) => {
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					      this.createHotspot(hotspot)
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createHotspot(hotspot: any) {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 使用JSON数据动态创建热点
 | 
				
			||||||
 | 
					    const position = new Vector3(
 | 
				
			||||||
 | 
					      hotspot.position.x,
 | 
				
			||||||
 | 
					      hotspot.position.y + this.offset,
 | 
				
			||||||
 | 
					      hotspot.position.z
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const disposition = new Vector3(
 | 
				
			||||||
 | 
					      hotspot.disposition.x,
 | 
				
			||||||
 | 
					      hotspot.disposition.y,
 | 
				
			||||||
 | 
					      hotspot.disposition.z
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.hotSpot.Point_Event(
 | 
				
			||||||
 | 
					      new HotspotPrams(
 | 
				
			||||||
 | 
					        position,
 | 
				
			||||||
 | 
					        disposition,
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        async (p: Point) => {
 | 
				
			||||||
 | 
					          // if (this.sllingPointStore.isOpen) return;
 | 
				
			||||||
 | 
					          this.sllingPointStore.setTitle(hotspot.title)
 | 
				
			||||||
 | 
					          this.sllingPointStore.setContent(hotspot.content)
 | 
				
			||||||
 | 
					          this.sllingPointStore.setIsOpen(true)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clean() {
 | 
				
			||||||
 | 
					    // 首先隐藏所有热点
 | 
				
			||||||
 | 
					    this.visible(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果存在热点池
 | 
				
			||||||
 | 
					    if (this.hotSpot && this.hotSpot._point_Pool && this.hotSpot._point_Pool.points) {
 | 
				
			||||||
 | 
					      // 遍历所有热点
 | 
				
			||||||
 | 
					      for (let i = 0; i < this.hotSpot._point_Pool.points.length; i++) {
 | 
				
			||||||
 | 
					        const point = this.hotSpot._point_Pool.points[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 清除事件监听器
 | 
				
			||||||
 | 
					        if (point.img && point.onCallBack) {
 | 
				
			||||||
 | 
					          point.img.removeEventListener('mousedown', point.onCallBack);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 从DOM中移除注释元素
 | 
				
			||||||
 | 
					        if (point.annotation && point.annotation.parentNode) {
 | 
				
			||||||
 | 
					          point.annotation.parentNode.removeChild(point.annotation);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 释放sprite资源
 | 
				
			||||||
 | 
					        if (point.sprite) {
 | 
				
			||||||
 | 
					          point.sprite.dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 清空热点池
 | 
				
			||||||
 | 
					      this.hotSpot._point_Pool.points = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log('热点资源已释放');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  visible(visible: boolean) {
 | 
				
			||||||
 | 
					    console.log(visible);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (this.hotSpot) {
 | 
				
			||||||
 | 
					      this.hotSpot.Enable_All(visible)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										410
									
								
								src/script/public/AppLight.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,410 @@
 | 
				
			|||||||
 | 
					import {  MainApp } from "@/script/core";
 | 
				
			||||||
 | 
					import { Monobehiver } from "@/script/base";
 | 
				
			||||||
 | 
					import { 
 | 
				
			||||||
 | 
					  Scene, 
 | 
				
			||||||
 | 
					  HemisphericLight, 
 | 
				
			||||||
 | 
					  DirectionalLight, 
 | 
				
			||||||
 | 
					  PointLight,
 | 
				
			||||||
 | 
					  SpotLight,
 | 
				
			||||||
 | 
					  Vector3, 
 | 
				
			||||||
 | 
					  Color3, 
 | 
				
			||||||
 | 
					  Light 
 | 
				
			||||||
 | 
					} from "@babylonjs/core";
 | 
				
			||||||
 | 
					import { GizmoManager, LightGizmo, UtilityLayerRenderer } from "@babylonjs/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 灯光类型枚举
 | 
				
			||||||
 | 
					export enum LightType {
 | 
				
			||||||
 | 
					  DIRECTIONAL = "DirectionalLight",
 | 
				
			||||||
 | 
					  POINT = "PointLight", 
 | 
				
			||||||
 | 
					  SPOT = "SpotLight",
 | 
				
			||||||
 | 
					  HEMISPHERIC = "HemisphericLight"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 灯光配置接口
 | 
				
			||||||
 | 
					interface LightConfig {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  type: LightType;
 | 
				
			||||||
 | 
					  direction?: Vector3;
 | 
				
			||||||
 | 
					  intensity: number;
 | 
				
			||||||
 | 
					  position: Vector3;
 | 
				
			||||||
 | 
					  color?: Color3;
 | 
				
			||||||
 | 
					  angle?: number; // 聚光灯角度
 | 
				
			||||||
 | 
					  exponent?: number; // 聚光灯指数
 | 
				
			||||||
 | 
					  range?: number; // 点光源范围
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppLight extends Monobehiver {
 | 
				
			||||||
 | 
					  lightList: Array<Light> = []
 | 
				
			||||||
 | 
					  private lightCounter: number = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 默认灯光配置
 | 
				
			||||||
 | 
					  private defaultLightConfigs: LightConfig[] = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: "onceDirectionalLight",
 | 
				
			||||||
 | 
					      type: LightType.DIRECTIONAL,
 | 
				
			||||||
 | 
					      direction: new Vector3(0, -1, 0),
 | 
				
			||||||
 | 
					      intensity: 1,
 | 
				
			||||||
 | 
					      position: new Vector3(0, 10, 5),
 | 
				
			||||||
 | 
					      color: new Color3(1, 1, 1)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: "secondDirectionalLight", 
 | 
				
			||||||
 | 
					      type: LightType.DIRECTIONAL,
 | 
				
			||||||
 | 
					      direction: new Vector3(1, -1, 0),
 | 
				
			||||||
 | 
					      intensity: 0.7,
 | 
				
			||||||
 | 
					      position: new Vector3(-5, 8, 2),
 | 
				
			||||||
 | 
					      color: new Color3(1, 1, 1)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: "yundongDirLight",
 | 
				
			||||||
 | 
					      type: LightType.DIRECTIONAL,
 | 
				
			||||||
 | 
					      direction: new Vector3(0, 1, 0),
 | 
				
			||||||
 | 
					      intensity: 0.2,
 | 
				
			||||||
 | 
					      position: new Vector3(0, 0, 0),
 | 
				
			||||||
 | 
					      color: new Color3(1, 1, 1)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Awake() {
 | 
				
			||||||
 | 
					    this.createDefaultLights(this.mainApp.appScene.object);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 创建默认灯光
 | 
				
			||||||
 | 
					  createDefaultLights(scene: Scene) {
 | 
				
			||||||
 | 
					    console.log("创建默认灯光配置");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.defaultLightConfigs.forEach(config => {
 | 
				
			||||||
 | 
					      this.createLight(scene, config);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 创建灯光的主方法
 | 
				
			||||||
 | 
					  createLight(scene: Scene, config: LightConfig): Light {
 | 
				
			||||||
 | 
					    let light: Light;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    switch (config.type) {
 | 
				
			||||||
 | 
					      case LightType.DIRECTIONAL:
 | 
				
			||||||
 | 
					        light = this.createDirectionalLight(scene, config);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case LightType.POINT:
 | 
				
			||||||
 | 
					        light = this.createPointLight(scene, config);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case LightType.SPOT:
 | 
				
			||||||
 | 
					        light = this.createSpotLight(scene, config);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case LightType.HEMISPHERIC:
 | 
				
			||||||
 | 
					        light = this.createHemisphericLight(scene, config);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        throw new Error(`不支持的灯光类型: ${config.type}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.lightList.push(light);
 | 
				
			||||||
 | 
					    console.log(`✅ 创建灯光: ${config.name} (${config.type})`);
 | 
				
			||||||
 | 
					    return light;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 创建平行光
 | 
				
			||||||
 | 
					  private createDirectionalLight(scene: Scene, config: LightConfig): DirectionalLight {
 | 
				
			||||||
 | 
					    const light = new DirectionalLight(config.name, config.direction || new Vector3(0, -1, 0), scene);
 | 
				
			||||||
 | 
					    light.intensity = config.intensity;
 | 
				
			||||||
 | 
					    light.position = config.position;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (config.color) {
 | 
				
			||||||
 | 
					      light.diffuse = config.color;
 | 
				
			||||||
 | 
					      light.specular = config.color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return light;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 创建点光源
 | 
				
			||||||
 | 
					  private createPointLight(scene: Scene, config: LightConfig): PointLight {
 | 
				
			||||||
 | 
					    const light = new PointLight(config.name, config.position, scene);
 | 
				
			||||||
 | 
					    light.intensity = config.intensity;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (config.range) {
 | 
				
			||||||
 | 
					      light.range = config.range;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (config.color) {
 | 
				
			||||||
 | 
					      light.diffuse = config.color;
 | 
				
			||||||
 | 
					      light.specular = config.color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return light;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 创建聚光灯
 | 
				
			||||||
 | 
					  private createSpotLight(scene: Scene, config: LightConfig): SpotLight {
 | 
				
			||||||
 | 
					    const light = new SpotLight(
 | 
				
			||||||
 | 
					      config.name, 
 | 
				
			||||||
 | 
					      config.position, 
 | 
				
			||||||
 | 
					      config.direction || new Vector3(0, -1, 0), 
 | 
				
			||||||
 | 
					      config.angle || Math.PI / 4, 
 | 
				
			||||||
 | 
					      config.exponent || 2, 
 | 
				
			||||||
 | 
					      scene
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    light.intensity = config.intensity;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (config.color) {
 | 
				
			||||||
 | 
					      light.diffuse = config.color;
 | 
				
			||||||
 | 
					      light.specular = config.color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return light;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 创建半球光
 | 
				
			||||||
 | 
					  private createHemisphericLight(scene: Scene, config: LightConfig): HemisphericLight {
 | 
				
			||||||
 | 
					    const light = new HemisphericLight(config.name, config.direction || new Vector3(0, 1, 0), scene);
 | 
				
			||||||
 | 
					    light.intensity = config.intensity;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (config.color) {
 | 
				
			||||||
 | 
					      light.diffuse = config.color;
 | 
				
			||||||
 | 
					      light.specular = config.color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return light;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 动态添加灯光
 | 
				
			||||||
 | 
					  addLight(type: LightType, options: {
 | 
				
			||||||
 | 
					    name?: string;
 | 
				
			||||||
 | 
					    position?: Vector3;
 | 
				
			||||||
 | 
					    direction?: Vector3;
 | 
				
			||||||
 | 
					    intensity?: number;
 | 
				
			||||||
 | 
					    color?: Color3;
 | 
				
			||||||
 | 
					    angle?: number;
 | 
				
			||||||
 | 
					    exponent?: number;
 | 
				
			||||||
 | 
					    range?: number;
 | 
				
			||||||
 | 
					  } = {}): Light {
 | 
				
			||||||
 | 
					    const scene = this.mainApp.appScene.object;
 | 
				
			||||||
 | 
					    if (!scene) {
 | 
				
			||||||
 | 
					      throw new Error("场景未初始化");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 生成默认名称
 | 
				
			||||||
 | 
					    const name = options.name || `${type}_${++this.lightCounter}`;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 检查名称是否已存在
 | 
				
			||||||
 | 
					    if (this.lightList.find(light => light.name === name)) {
 | 
				
			||||||
 | 
					      throw new Error(`灯光名称已存在: ${name}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const config: LightConfig = {
 | 
				
			||||||
 | 
					      name,
 | 
				
			||||||
 | 
					      type,
 | 
				
			||||||
 | 
					      position: options.position || new Vector3(0, 5, 0),
 | 
				
			||||||
 | 
					      direction: options.direction,
 | 
				
			||||||
 | 
					      intensity: options.intensity || 1,
 | 
				
			||||||
 | 
					      color: options.color || new Color3(1, 1, 1),
 | 
				
			||||||
 | 
					      angle: options.angle,
 | 
				
			||||||
 | 
					      exponent: options.exponent,
 | 
				
			||||||
 | 
					      range: options.range
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return this.createLight(scene, config);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 删除灯光
 | 
				
			||||||
 | 
					  removeLight(lightName: string): boolean {
 | 
				
			||||||
 | 
					    const lightIndex = this.lightList.findIndex(light => light.name === lightName);
 | 
				
			||||||
 | 
					    if (lightIndex === -1) {
 | 
				
			||||||
 | 
					      console.warn(`灯光不存在: ${lightName}`);
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const light = this.lightList[lightIndex];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 从场景中移除
 | 
				
			||||||
 | 
					    light.dispose();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 从列表中移除
 | 
				
			||||||
 | 
					    this.lightList.splice(lightIndex, 1);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`🗑️ 删除灯光: ${lightName}`);
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 获取灯光类型
 | 
				
			||||||
 | 
					  getLightType(light: Light): LightType {
 | 
				
			||||||
 | 
					    if (light instanceof DirectionalLight) return LightType.DIRECTIONAL;
 | 
				
			||||||
 | 
					    if (light instanceof PointLight) return LightType.POINT;
 | 
				
			||||||
 | 
					    if (light instanceof SpotLight) return LightType.SPOT;
 | 
				
			||||||
 | 
					    if (light instanceof HemisphericLight) return LightType.HEMISPHERIC;
 | 
				
			||||||
 | 
					    throw new Error(`未知的灯光类型: ${light.constructor.name}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 获取所有灯光信息(增强版)
 | 
				
			||||||
 | 
					  getAllLights() {
 | 
				
			||||||
 | 
					    return this.lightList.map((light) => {
 | 
				
			||||||
 | 
					      const lightType = this.getLightType(light);
 | 
				
			||||||
 | 
					      const baseInfo = {
 | 
				
			||||||
 | 
					        id: light.id,
 | 
				
			||||||
 | 
					        name: light.name,
 | 
				
			||||||
 | 
					        type: lightType,
 | 
				
			||||||
 | 
					        intensity: light.intensity,
 | 
				
			||||||
 | 
					        position: (light as any).position || new Vector3(0, 0, 0),
 | 
				
			||||||
 | 
					        color: {
 | 
				
			||||||
 | 
					          r: light.diffuse.r,
 | 
				
			||||||
 | 
					          g: light.diffuse.g,
 | 
				
			||||||
 | 
					          b: light.diffuse.b
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        enabled: light.isEnabled()
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 根据灯光类型添加特定属性
 | 
				
			||||||
 | 
					      switch (lightType) {
 | 
				
			||||||
 | 
					        case LightType.DIRECTIONAL:
 | 
				
			||||||
 | 
					        case LightType.SPOT:
 | 
				
			||||||
 | 
					          return {
 | 
				
			||||||
 | 
					            ...baseInfo,
 | 
				
			||||||
 | 
					            direction: (light as DirectionalLight | SpotLight).direction
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        case LightType.POINT:
 | 
				
			||||||
 | 
					          return {
 | 
				
			||||||
 | 
					            ...baseInfo,
 | 
				
			||||||
 | 
					            range: (light as PointLight).range
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        case LightType.SPOT:
 | 
				
			||||||
 | 
					          return {
 | 
				
			||||||
 | 
					            ...baseInfo,
 | 
				
			||||||
 | 
					            direction: (light as SpotLight).direction,
 | 
				
			||||||
 | 
					            angle: (light as SpotLight).angle,
 | 
				
			||||||
 | 
					            exponent: (light as SpotLight).exponent
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          return baseInfo;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 根据名称修改灯光属性(增强版)
 | 
				
			||||||
 | 
					  updateLight(name: string, properties: {
 | 
				
			||||||
 | 
					    position?: Vector3 | { x: number; y: number; z: number };
 | 
				
			||||||
 | 
					    direction?: Vector3 | { x: number; y: number; z: number };
 | 
				
			||||||
 | 
					    intensity?: number;
 | 
				
			||||||
 | 
					    color?: { r: number; g: number; b: number };
 | 
				
			||||||
 | 
					    range?: number;
 | 
				
			||||||
 | 
					    angle?: number;
 | 
				
			||||||
 | 
					    exponent?: number;
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    const targetLight = this.lightList.find(light => light.name === name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!targetLight) {
 | 
				
			||||||
 | 
					      console.warn(`灯光不存在: ${name}`);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(`🔧 更新灯光 ${name}:`, properties);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新位置
 | 
				
			||||||
 | 
					    if (properties.position && (targetLight as any).position) {
 | 
				
			||||||
 | 
					      if (properties.position instanceof Vector3) {
 | 
				
			||||||
 | 
					        (targetLight as any).position = properties.position.clone();
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        (targetLight as any).position = new Vector3(properties.position.x, properties.position.y, properties.position.z);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      console.log(`📍 位置已更新:`, (targetLight as any).position);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新方向(仅适用于平行光和聚光灯)
 | 
				
			||||||
 | 
					    if (properties.direction && (targetLight instanceof DirectionalLight || targetLight instanceof SpotLight)) {
 | 
				
			||||||
 | 
					      let newDirection: Vector3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (properties.direction instanceof Vector3) {
 | 
				
			||||||
 | 
					        newDirection = properties.direction.clone();
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        newDirection = new Vector3(properties.direction.x, properties.direction.y, properties.direction.z);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (newDirection.length() > 0.001) {
 | 
				
			||||||
 | 
					        newDirection.normalize();
 | 
				
			||||||
 | 
					        targetLight.direction = newDirection;
 | 
				
			||||||
 | 
					        console.log(`🎯 方向已更新:`, newDirection);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        console.warn(`⚠️ 方向向量长度为0,跳过更新`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新强度
 | 
				
			||||||
 | 
					    if (typeof properties.intensity === 'number') {
 | 
				
			||||||
 | 
					      targetLight.intensity = properties.intensity;
 | 
				
			||||||
 | 
					      console.log(`💡 强度已更新:`, properties.intensity);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新颜色
 | 
				
			||||||
 | 
					    if (properties.color) {
 | 
				
			||||||
 | 
					      targetLight.diffuse = new Color3(properties.color.r, properties.color.g, properties.color.b);
 | 
				
			||||||
 | 
					      targetLight.specular = new Color3(properties.color.r, properties.color.g, properties.color.b);
 | 
				
			||||||
 | 
					      console.log(`🎨 颜色已更新:`, properties.color);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新点光源范围
 | 
				
			||||||
 | 
					    if (typeof properties.range === 'number' && targetLight instanceof PointLight) {
 | 
				
			||||||
 | 
					      targetLight.range = properties.range;
 | 
				
			||||||
 | 
					      console.log(`📏 范围已更新:`, properties.range);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新聚光灯属性
 | 
				
			||||||
 | 
					    if (targetLight instanceof SpotLight) {
 | 
				
			||||||
 | 
					      if (typeof properties.angle === 'number') {
 | 
				
			||||||
 | 
					        targetLight.angle = properties.angle;
 | 
				
			||||||
 | 
					        console.log(`🔦 角度已更新:`, properties.angle);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (typeof properties.exponent === 'number') {
 | 
				
			||||||
 | 
					        targetLight.exponent = properties.exponent;
 | 
				
			||||||
 | 
					        console.log(`📐 指数已更新:`, properties.exponent);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 强制刷新场景渲染
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const scene = this.mainApp.appScene.object;
 | 
				
			||||||
 | 
					      if (scene) {
 | 
				
			||||||
 | 
					        scene.markAllMaterialsAsDirty();
 | 
				
			||||||
 | 
					        scene.render();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.warn(`场景刷新失败:`, error);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 保持向后兼容的方法
 | 
				
			||||||
 | 
					  setDirectionalLight(light: any) {
 | 
				
			||||||
 | 
					    console.log(light);
 | 
				
			||||||
 | 
					    // 这个方法现在需要根据具体的灯光名称来更新
 | 
				
			||||||
 | 
					    // 保持原有逻辑但需要适配新的结构
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setIntensity(light: any) {
 | 
				
			||||||
 | 
					    // 这个方法现在需要根据具体的灯光名称来更新
 | 
				
			||||||
 | 
					    // 保持原有逻辑但需要适配新的结构
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  excludedMeshesCar(mesh: any) {
 | 
				
			||||||
 | 
					    // 为所有灯光排除车辆网格
 | 
				
			||||||
 | 
					    this.lightList.forEach(light => {
 | 
				
			||||||
 | 
					      if (light.excludedMeshes) {
 | 
				
			||||||
 | 
					        light.excludedMeshes.push(mesh);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  excludedMeshesScene(mesh: any) {
 | 
				
			||||||
 | 
					    // 为所有灯光排除场景网格
 | 
				
			||||||
 | 
					    this.lightList.forEach(light => {
 | 
				
			||||||
 | 
					      if (light.excludedMeshes) {
 | 
				
			||||||
 | 
					        light.excludedMeshes.push(mesh);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AppLight };
 | 
				
			||||||
							
								
								
									
										70
									
								
								src/script/public/AppModel.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					import { AbstractMesh, ImportMeshAsync } from "@babylonjs/core";
 | 
				
			||||||
 | 
					import { AppConfig } from "@/script/public/AppConfig";
 | 
				
			||||||
 | 
					import { Dictionary } from "@/script/utils/Dictionary";
 | 
				
			||||||
 | 
					import { MainApp } from "@/script/public/MainApp";
 | 
				
			||||||
 | 
					import "@babylonjs/loaders/gltf";
 | 
				
			||||||
 | 
					import { Monobehiver } from "@/script/base";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ModelLoadResult {
 | 
				
			||||||
 | 
					  success: boolean;
 | 
				
			||||||
 | 
					  meshes?: AbstractMesh[];
 | 
				
			||||||
 | 
					  skeletons?: any[];
 | 
				
			||||||
 | 
					  error?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppModel extends Monobehiver {
 | 
				
			||||||
 | 
					  // 模型字典,存储已加载的模型
 | 
				
			||||||
 | 
					  private modelDic: Dictionary<AbstractMesh[]> = new Dictionary();
 | 
				
			||||||
 | 
					  // 已加载的网格列表
 | 
				
			||||||
 | 
					  private loadedMeshes: AbstractMesh[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 加载所有模型
 | 
				
			||||||
 | 
					  async loadModel(): Promise<void> {
 | 
				
			||||||
 | 
					    const modelUrlList = AppConfig.modelUrlList;
 | 
				
			||||||
 | 
					    if (!modelUrlList?.length) return;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    const loadPromises = modelUrlList.map(url => this.loadSingleModel(url));
 | 
				
			||||||
 | 
					    await Promise.allSettled(loadPromises);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 模型加载完成后,应用定制化材质
 | 
				
			||||||
 | 
					    if (this.mainApp.appCustomization) {
 | 
				
			||||||
 | 
					      // 延迟一点时间,确保材质已加载
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        this.mainApp.appCustomization.applyToAllMeshes();
 | 
				
			||||||
 | 
					      }, 100);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // 加载单个模型文件
 | 
				
			||||||
 | 
					  private async loadSingleModel(modelUrl: string): Promise<ModelLoadResult> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const { meshes, skeletons } = await ImportMeshAsync(modelUrl, this.mainApp.appScene.object);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if (!meshes?.length) {
 | 
				
			||||||
 | 
					        return { success: false, error: '未找到网格' };
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 将网格添加到已加载列表和模型字典
 | 
				
			||||||
 | 
					      this.loadedMeshes.push(...meshes);
 | 
				
			||||||
 | 
					      this.modelDic.Set(modelUrl, meshes);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      return { success: true, meshes, skeletons };
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      return { success: false, error: error instanceof Error ? error.message : String(error) };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AppModel };
 | 
				
			||||||
							
								
								
									
										221
									
								
								src/script/public/AppRay.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,221 @@
 | 
				
			|||||||
 | 
					import  {
 | 
				
			||||||
 | 
					 type IPointerEvent,
 | 
				
			||||||
 | 
					  PickingInfo,
 | 
				
			||||||
 | 
					  PointerEventTypes,
 | 
				
			||||||
 | 
					  Vector3,
 | 
				
			||||||
 | 
					  AbstractMesh,
 | 
				
			||||||
 | 
					  Color3,
 | 
				
			||||||
 | 
					  PBRMaterial,
 | 
				
			||||||
 | 
					  StandardMaterial,
 | 
				
			||||||
 | 
					  HighlightLayer,
 | 
				
			||||||
 | 
					  PointerInfo,
 | 
				
			||||||
 | 
					} from '@babylonjs/core'
 | 
				
			||||||
 | 
					import { MainApp } from './MainApp'
 | 
				
			||||||
 | 
					import { Monobehiver } from '@/script/base'
 | 
				
			||||||
 | 
					import { useMaterialStore } from '@/stores/material'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppRay extends Monobehiver {
 | 
				
			||||||
 | 
					  oldPoint: Vector3 = Vector3.Zero()
 | 
				
			||||||
 | 
					  newPoint: Vector3 = Vector3.Zero()
 | 
				
			||||||
 | 
					  private materialStore = useMaterialStore()
 | 
				
			||||||
 | 
					  private highlightLayer: HighlightLayer | null = null
 | 
				
			||||||
 | 
					  private originalMaterial: any = null
 | 
				
			||||||
 | 
					  private highlightedMesh: AbstractMesh | null = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Awake() {
 | 
				
			||||||
 | 
					    this.setupHighlightLayer()
 | 
				
			||||||
 | 
					    this.setupUnifiedEventHandling()
 | 
				
			||||||
 | 
					    this.debugMeshPickableStatus()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 设置高亮层
 | 
				
			||||||
 | 
					  setupHighlightLayer() {
 | 
				
			||||||
 | 
					    // 高亮层创建已禁用
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 设置统一的事件处理
 | 
				
			||||||
 | 
					  setupUnifiedEventHandling() {
 | 
				
			||||||
 | 
					    // 使用观察者模式而不是直接覆盖事件处理器
 | 
				
			||||||
 | 
					    this.mainApp.appScene.object.onPointerObservable.add((pointerInfo: PointerInfo) => {
 | 
				
			||||||
 | 
					      const { type, event, pickInfo } = pointerInfo;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 检查事件类型并转换
 | 
				
			||||||
 | 
					      const pointerEvent = event as IPointerEvent;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 只处理鼠标和触摸事件
 | 
				
			||||||
 | 
					      if (pointerEvent.pointerType !== "mouse" && pointerEvent.pointerType !== "touch") {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 处理非主要触摸点
 | 
				
			||||||
 | 
					      if (pointerEvent.pointerType === "touch" && (pointerEvent as any).isPrimary === false) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (type === PointerEventTypes.POINTERDOWN) {
 | 
				
			||||||
 | 
					        this.oldPoint.set(pointerEvent.clientX, 0, pointerEvent.clientY);
 | 
				
			||||||
 | 
					      } else if (type === PointerEventTypes.POINTERUP) {
 | 
				
			||||||
 | 
					        this.newPoint.set(pointerEvent.clientX, 0, pointerEvent.clientY);
 | 
				
			||||||
 | 
					        const distance = Vector3.Distance(this.oldPoint, this.newPoint);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 只有在没有移动的情况下才处理单击
 | 
				
			||||||
 | 
					        if (distance < 5) { // 增加一些容差
 | 
				
			||||||
 | 
					          this.handleSingleClick(pointerEvent, pickInfo);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 处理单击
 | 
				
			||||||
 | 
					  handleSingleClick(evt: IPointerEvent, pickInfo: PickingInfo | null) {
 | 
				
			||||||
 | 
					    console.log('单击事件触发', { pickInfo, mesh: pickInfo?.pickedMesh?.name });
 | 
				
			||||||
 | 
					    if (pickInfo && pickInfo.pickedMesh) {
 | 
				
			||||||
 | 
					      this.handleMaterialSelection(pickInfo.pickedMesh as AbstractMesh);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 处理材质选择
 | 
				
			||||||
 | 
					  handleMaterialSelection(mesh: AbstractMesh) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      console.log('开始处理材质选择:', mesh.name);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 清除之前的高亮
 | 
				
			||||||
 | 
					      this.clearHighlight()
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 获取材质信息
 | 
				
			||||||
 | 
					      const materialInfo = this.materialStore.getMaterialInfo(mesh)
 | 
				
			||||||
 | 
					      console.log('材质信息:', materialInfo);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 设置选中的材质
 | 
				
			||||||
 | 
					      this.materialStore.setSelectedMaterial(materialInfo.materialName)
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 高亮显示选中的网格
 | 
				
			||||||
 | 
					      this.highlightMesh(mesh)
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      console.log('材质已选择:', {
 | 
				
			||||||
 | 
					        meshName: materialInfo.meshName,
 | 
				
			||||||
 | 
					        materialName: materialInfo.materialName,
 | 
				
			||||||
 | 
					        hasMaterial: !!materialInfo.material
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('选择材质失败:', error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 高亮显示网格 - 已禁用
 | 
				
			||||||
 | 
					  highlightMesh(mesh: AbstractMesh) {
 | 
				
			||||||
 | 
					    // 高亮功能已禁用
 | 
				
			||||||
 | 
					    console.log('高亮功能已禁用,跳过高亮:', mesh.name)
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 使用材质方式高亮 - 已禁用
 | 
				
			||||||
 | 
					  highlightWithMaterial(mesh: AbstractMesh) {
 | 
				
			||||||
 | 
					    // 材质高亮功能已禁用
 | 
				
			||||||
 | 
					    console.log('材质高亮功能已禁用,跳过:', mesh.name)
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 清除高亮
 | 
				
			||||||
 | 
					  clearHighlight() {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // 清除高亮层
 | 
				
			||||||
 | 
					      if (this.highlightLayer && this.highlightedMesh) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          this.highlightLayer.removeMesh(this.highlightedMesh as any)
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					          console.warn('高亮层移除失败:', error)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // 恢复原始材质
 | 
				
			||||||
 | 
					      if (this.highlightedMesh && this.originalMaterial) {
 | 
				
			||||||
 | 
					        const material = this.highlightedMesh.material as PBRMaterial
 | 
				
			||||||
 | 
					        if (material && this.originalMaterial.albedoColor) {
 | 
				
			||||||
 | 
					          material.albedoColor = this.originalMaterial.albedoColor
 | 
				
			||||||
 | 
					          material.emissiveColor = this.originalMaterial.emissiveColor
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      this.highlightedMesh = null
 | 
				
			||||||
 | 
					      this.originalMaterial = null
 | 
				
			||||||
 | 
					      this.materialStore.setHighlighted(false)
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('清除高亮失败:', error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 调试网格点击状态
 | 
				
			||||||
 | 
					  debugMeshPickableStatus() {
 | 
				
			||||||
 | 
					    console.log('=== 调试网格点击状态 ===');
 | 
				
			||||||
 | 
					    const scene = this.mainApp.appScene.object;
 | 
				
			||||||
 | 
					    if (!scene) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const allMeshes = scene.meshes;
 | 
				
			||||||
 | 
					    const hairMeshes: AbstractMesh[] = [];
 | 
				
			||||||
 | 
					    const pickableMeshes: AbstractMesh[] = [];
 | 
				
			||||||
 | 
					    const nonPickableMeshes: AbstractMesh[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    allMeshes.forEach((mesh: AbstractMesh) => {
 | 
				
			||||||
 | 
					      // 检查是否是头发相关的网格
 | 
				
			||||||
 | 
					      if (mesh.name.toLowerCase().includes('hair') || 
 | 
				
			||||||
 | 
					          mesh.name.toLowerCase().includes('head') ||
 | 
				
			||||||
 | 
					          mesh.name.includes('Hair')) {
 | 
				
			||||||
 | 
					        hairMeshes.push(mesh);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (mesh.isPickable) {
 | 
				
			||||||
 | 
					        pickableMeshes.push(mesh);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        nonPickableMeshes.push(mesh);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 详细输出头发网格信息
 | 
				
			||||||
 | 
					    if (hairMeshes.length > 0) {
 | 
				
			||||||
 | 
					      hairMeshes.forEach((mesh: AbstractMesh, index: number) => {
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 输出不可点击的网格
 | 
				
			||||||
 | 
					    if (nonPickableMeshes.length > 0) {
 | 
				
			||||||
 | 
					      nonPickableMeshes.forEach((mesh, index) => {
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 强制设置头发网格为可点击
 | 
				
			||||||
 | 
					  fixHairMeshPickable() {
 | 
				
			||||||
 | 
					    console.log('=== 修复头发网格点击状态 ===');
 | 
				
			||||||
 | 
					    const scene = this.mainApp.appScene.object;
 | 
				
			||||||
 | 
					    if (!scene) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let fixedCount = 0;
 | 
				
			||||||
 | 
					    scene.meshes.forEach((mesh: AbstractMesh) => {
 | 
				
			||||||
 | 
					      // 检查是否是头发相关的网格
 | 
				
			||||||
 | 
					      if (mesh.name.toLowerCase().includes('hair') || 
 | 
				
			||||||
 | 
					          mesh.name.toLowerCase().includes('head') ||
 | 
				
			||||||
 | 
					          mesh.name.includes('Hair')) {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (!mesh.isPickable) {
 | 
				
			||||||
 | 
					          mesh.isPickable = true;
 | 
				
			||||||
 | 
					          fixedCount++;
 | 
				
			||||||
 | 
					          console.log(`已修复头发网格: ${mesh.name}`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(`共修复了 ${fixedCount} 个头发网格的点击状态`);
 | 
				
			||||||
 | 
					    console.log('========================');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AppRay }
 | 
				
			||||||
							
								
								
									
										45
									
								
								src/script/public/AppScene.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					import { Color4, Scene, PointerEventTypes, ImageProcessingConfiguration } from '@babylonjs/core'
 | 
				
			||||||
 | 
					import { MainApp } from '@/script/core'
 | 
				
			||||||
 | 
					import { Monobehiver } from '@/script/base'
 | 
				
			||||||
 | 
					import { userDragStore, userBottomNavigationStore } from '@/stores/zguiy'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppScene extends Monobehiver {
 | 
				
			||||||
 | 
					  object!: Scene
 | 
				
			||||||
 | 
					  isComplate!: boolean
 | 
				
			||||||
 | 
					  dragStore = userDragStore()
 | 
				
			||||||
 | 
					  // 鼠标事件
 | 
				
			||||||
 | 
					  isMouseDown = false;
 | 
				
			||||||
 | 
					  bottomNavigationStore: any
 | 
				
			||||||
 | 
					  dragTimeout: any = null;
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Awake() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.bottomNavigationStore = userBottomNavigationStore()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.object = new Scene(this.mainApp.appEngin.object)
 | 
				
			||||||
 | 
					    this.object.autoClear = true
 | 
				
			||||||
 | 
					    this.object.clearColor = new Color4(0, 0, 0, 0)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 优化设置,防止动画卡住
 | 
				
			||||||
 | 
					    this.object.autoClear = true
 | 
				
			||||||
 | 
					    this.object.blockMaterialDirtyMechanism = false // 允许材质更新
 | 
				
			||||||
 | 
					    this.object.forceShowBoundingBoxes = false // 不强制显示边界框
 | 
				
			||||||
 | 
					    this.object.skipFrustumClipping = true // 禁用视锥裁剪,防止模型显示不全时蒙皮消失
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getScene() {
 | 
				
			||||||
 | 
					    return this.object
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  update() {
 | 
				
			||||||
 | 
					    this.object.render()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  clean() {
 | 
				
			||||||
 | 
					    this.object && this.object.dispose()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export { AppScene }
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/script/public/AppTween.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import { animate,easeOut } from "popmotion"
 | 
				
			||||||
 | 
					import { MainApp } from "@/script/core"
 | 
				
			||||||
 | 
					import { Monobehiver } from "@/script/base"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AppTween extends Monobehiver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					        super(mainApp)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    run(form: any, to: any,update:Function,complete:Function,timer=1000) {
 | 
				
			||||||
 | 
					        animate({
 | 
				
			||||||
 | 
					            from: form,
 | 
				
			||||||
 | 
					            to: to,
 | 
				
			||||||
 | 
					            duration: timer,
 | 
				
			||||||
 | 
					            ease:easeOut,  
 | 
				
			||||||
 | 
					            onUpdate: latest => update(latest),
 | 
				
			||||||
 | 
					            onComplete: () => {
 | 
				
			||||||
 | 
					                complete()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/script/public/GameManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					import { AbstractMesh, Color3, PBRMaterial } from "@babylonjs/core";
 | 
				
			||||||
 | 
					import { Monobehiver } from "@/script/base";
 | 
				
			||||||
 | 
					import type { MainApp } from "./MainApp";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GameManager extends Monobehiver {
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					    super(mainApp)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // 主角色模型引用
 | 
				
			||||||
 | 
					  private yifu: AbstractMesh | null = null;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { GameManager };
 | 
				
			||||||
							
								
								
									
										183
									
								
								src/script/public/MainApp.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					  AppCamera,
 | 
				
			||||||
 | 
					  AppEnv,
 | 
				
			||||||
 | 
					  AppEngin,
 | 
				
			||||||
 | 
					  AppScene,
 | 
				
			||||||
 | 
					  GameManager,
 | 
				
			||||||
 | 
					  AppConfig,
 | 
				
			||||||
 | 
					  AppDom,
 | 
				
			||||||
 | 
					  AppTween,
 | 
				
			||||||
 | 
					  AppRay,
 | 
				
			||||||
 | 
					  Dimensions,
 | 
				
			||||||
 | 
					  AppHotspot,
 | 
				
			||||||
 | 
					  AppGlowLayer,
 | 
				
			||||||
 | 
					  AppLight,
 | 
				
			||||||
 | 
					  AppCustomization,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} from "../core";
 | 
				
			||||||
 | 
					import { AppModel } from '@/script/public/AppModel';
 | 
				
			||||||
 | 
					import { DracoCompression, KhronosTextureContainer2, MeshBuilder, PBRMaterial, PBRMetallicRoughnessMaterial, SceneOptimizer, SceneOptimizerOptions, StandardMaterial } from "@babylonjs/core";
 | 
				
			||||||
 | 
					import { userLoadingStore, userCarStore, userCarModelStore } from '@/stores/zguiy';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MainApp {
 | 
				
			||||||
 | 
					  canvas!: HTMLCanvasElement;
 | 
				
			||||||
 | 
					  appEngin!: AppEngin;
 | 
				
			||||||
 | 
					  appScene!: AppScene;
 | 
				
			||||||
 | 
					  appCamera!: AppCamera;
 | 
				
			||||||
 | 
					  gameManager!: GameManager;
 | 
				
			||||||
 | 
					  appDom!: AppDom;
 | 
				
			||||||
 | 
					  appTween!: AppTween;
 | 
				
			||||||
 | 
					  appModel!: AppModel;
 | 
				
			||||||
 | 
					  appEnv!: AppEnv;
 | 
				
			||||||
 | 
					  appRay!: AppRay
 | 
				
			||||||
 | 
					  isRender: boolean = true;
 | 
				
			||||||
 | 
					  setInterval: any
 | 
				
			||||||
 | 
					  dimensions!: Dimensions
 | 
				
			||||||
 | 
					  carStore!: any
 | 
				
			||||||
 | 
					  carModelStore!: any
 | 
				
			||||||
 | 
					  appGlowLayer!: AppGlowLayer
 | 
				
			||||||
 | 
					  appGlowLayer1!: AppGlowLayer
 | 
				
			||||||
 | 
					  appLight!: AppLight
 | 
				
			||||||
 | 
					  appCustomization!: AppCustomization
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.carStore = userCarStore()
 | 
				
			||||||
 | 
					    this.carModelStore = userCarModelStore()
 | 
				
			||||||
 | 
					    this.appDom = new AppDom();
 | 
				
			||||||
 | 
					    this.appEngin = new AppEngin(this);
 | 
				
			||||||
 | 
					    this.appScene = new AppScene(this);
 | 
				
			||||||
 | 
					    this.appCamera = new AppCamera(this);
 | 
				
			||||||
 | 
					    this.gameManager = new GameManager(this);
 | 
				
			||||||
 | 
					    this.appTween = new AppTween(this);
 | 
				
			||||||
 | 
					    this.appModel = new AppModel(this);
 | 
				
			||||||
 | 
					    this.appEnv = new AppEnv(this);
 | 
				
			||||||
 | 
					    this.appRay = new AppRay(this);
 | 
				
			||||||
 | 
					    this.dimensions = new Dimensions(this);
 | 
				
			||||||
 | 
					    this.appGlowLayer = new AppGlowLayer(this)
 | 
				
			||||||
 | 
					    this.appLight = new AppLight(this)
 | 
				
			||||||
 | 
					    this.appCustomization = new AppCustomization(this)
 | 
				
			||||||
 | 
					    window.addEventListener("resize", this.handleResize.bind(this), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loadAConfig(appConfig: any) {
 | 
				
			||||||
 | 
					    AppConfig.modelUrlList = appConfig.modelUrlList;
 | 
				
			||||||
 | 
					    AppConfig.success = appConfig.success || [];
 | 
				
			||||||
 | 
					    AppConfig.animationUrlList = appConfig.animationUrlList || [];
 | 
				
			||||||
 | 
					    AppConfig.error = appConfig.error;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async Awake() {
 | 
				
			||||||
 | 
					    this.loadBabylon()
 | 
				
			||||||
 | 
					    this.appDom.Awake();
 | 
				
			||||||
 | 
					    this.appEngin.Awake();
 | 
				
			||||||
 | 
					    this.appScene.Awake();
 | 
				
			||||||
 | 
					    this.appCamera.Awake();
 | 
				
			||||||
 | 
					    // this.gameManager.Awake();
 | 
				
			||||||
 | 
					    this.appEnv.Awake();
 | 
				
			||||||
 | 
					    this.appModel.Awake();
 | 
				
			||||||
 | 
					    this.appGlowLayer.Awake();
 | 
				
			||||||
 | 
					    this.appLight.Awake();
 | 
				
			||||||
 | 
					    this.appCustomization.Awake();
 | 
				
			||||||
 | 
					    this.appRay.Awake();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.update()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loadBabylon() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DracoCompression.Configuration.decoder.wasmUrl = import.meta.env.BASE_URL + 'bblcdn/draco_wasm_wrapper_gltf.js';
 | 
				
			||||||
 | 
					    DracoCompression.Configuration.decoder.wasmBinaryUrl = import.meta.env.BASE_URL + 'bblcdn/draco_decoder_gltf.wasm'
 | 
				
			||||||
 | 
					    DracoCompression.Configuration.decoder.fallbackUrl = import.meta.env.BASE_URL + 'bblcdn/draco_decoder_gltf.js'
 | 
				
			||||||
 | 
					    KhronosTextureContainer2.URLConfig.jsDecoderModule = import.meta.env.BASE_URL + ' bblcdn/babylon.ktx2Decoder.js';
 | 
				
			||||||
 | 
					    KhronosTextureContainer2.URLConfig.wasmMSCTranscoder = import.meta.env.BASE_URL + 'bblcdn/msc_basis_transcoder.wasm'
 | 
				
			||||||
 | 
					    KhronosTextureContainer2.URLConfig.jsMSCTranscoder = import.meta.env.BASE_URL + 'bblcdn/msc_basis_transcoder.js'
 | 
				
			||||||
 | 
					    KhronosTextureContainer2.URLConfig.wasmUASTCToBC7 = import.meta.env.BASE_URL + 'bblcdn/uastc_bc7.wasm'
 | 
				
			||||||
 | 
					    KhronosTextureContainer2.URLConfig.wasmZSTDDecoder = import.meta.env.BASE_URL + 'bblcdn/zstddec.wasm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async loadModel() {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      this.loadComplate();
 | 
				
			||||||
 | 
					      await this.appModel.loadModel();
 | 
				
			||||||
 | 
					      // await this.gameManager.Awake();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      AppConfig.success();
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('加载模型过程中出错:', error);
 | 
				
			||||||
 | 
					      if (AppConfig.error) {
 | 
				
			||||||
 | 
					        AppConfig.error(error);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async loadAnimation() {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // await this.gameManager.Awake();
 | 
				
			||||||
 | 
					      AppConfig.success();
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('加载模型过程中出错:', error);
 | 
				
			||||||
 | 
					      if (AppConfig.error) {
 | 
				
			||||||
 | 
					        AppConfig.error(error);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  loadComplate() {
 | 
				
			||||||
 | 
					    // 使用直接导入的方式
 | 
				
			||||||
 | 
					    const loadingStore = userLoadingStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.setInterval) {
 | 
				
			||||||
 | 
					      clearInterval(this.setInterval);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.setInterval = setInterval(() => {
 | 
				
			||||||
 | 
					      // 获取当前进度值
 | 
				
			||||||
 | 
					      const currentProgress = loadingStore.loadProgress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 如果当前进度小于98,继续增加
 | 
				
			||||||
 | 
					      if (currentProgress < 98) {
 | 
				
			||||||
 | 
					        // 缓慢增加进度,根据当前值确定增量
 | 
				
			||||||
 | 
					        const increment = Math.max(0.1, Math.min(0.5, (98 - currentProgress) / 50));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.appDom.loadsliderbar.value = currentProgress + increment;
 | 
				
			||||||
 | 
					        loadingStore.setLoadProgress(this.appDom.loadsliderbar.value);
 | 
				
			||||||
 | 
					        // 同步更新store中的加载进度
 | 
				
			||||||
 | 
					        //  loadingStore.setLoadProgress(this.appDom.loadsliderbar.value);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, 50);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleResize() {
 | 
				
			||||||
 | 
					    this.appEngin.handleResize();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dispose() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      resolve(true)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  update() {
 | 
				
			||||||
 | 
					    this.appEngin.object.runRenderLoop(() => {
 | 
				
			||||||
 | 
					      this.appScene.object.render();
 | 
				
			||||||
 | 
					      this.appCamera.update();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  unUpdate() {
 | 
				
			||||||
 | 
					    this.appEngin.object.stopRenderLoop();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { MainApp };
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/script/utils/Axes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import { Scene, Vector3, MeshBuilder, Color3, StandardMaterial } from "@babylonjs/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 创建坐标轴辅助函数
 | 
				
			||||||
 | 
					export const createAxes=(scene: Scene, size: number = 10) => {
 | 
				
			||||||
 | 
					  // X轴 - 红色
 | 
				
			||||||
 | 
					  const xAxis = MeshBuilder.CreateLines("xAxis", {
 | 
				
			||||||
 | 
					    points: [Vector3.Zero(), new Vector3(size, 0, 0)],
 | 
				
			||||||
 | 
					  }, scene);
 | 
				
			||||||
 | 
					  const xMat = new StandardMaterial("xMat", scene);
 | 
				
			||||||
 | 
					  xMat.diffuseColor = Color3.Red();
 | 
				
			||||||
 | 
					  xAxis.color = Color3.Red();
 | 
				
			||||||
 | 
					  xAxis.material = xMat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Y轴 - 绿色
 | 
				
			||||||
 | 
					  const yAxis = MeshBuilder.CreateLines("yAxis", {
 | 
				
			||||||
 | 
					    points: [Vector3.Zero(), new Vector3(0, size, 0)],
 | 
				
			||||||
 | 
					  }, scene);
 | 
				
			||||||
 | 
					  const yMat = new StandardMaterial("yMat", scene);
 | 
				
			||||||
 | 
					  yMat.diffuseColor = Color3.Green();
 | 
				
			||||||
 | 
					  yAxis.color = Color3.Green();
 | 
				
			||||||
 | 
					  yAxis.material = yMat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Z轴 - 蓝色
 | 
				
			||||||
 | 
					  const zAxis = MeshBuilder.CreateLines("zAxis", {
 | 
				
			||||||
 | 
					    points: [Vector3.Zero(), new Vector3(0, 0, size)],
 | 
				
			||||||
 | 
					  }, scene);
 | 
				
			||||||
 | 
					  const zMat = new StandardMaterial("zMat", scene);
 | 
				
			||||||
 | 
					  zMat.diffuseColor = Color3.Blue();
 | 
				
			||||||
 | 
					  zAxis.color = Color3.Blue();
 | 
				
			||||||
 | 
					  zAxis.material = zMat;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/script/utils/ConfigLoading.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import { AppConfig } from "@/script/public/AppConfig";
 | 
				
			||||||
 | 
					export class ConfigLoading {
 | 
				
			||||||
 | 
					    static async load(configPaths: string[]) {
 | 
				
			||||||
 | 
					        const results = await Promise.all(
 | 
				
			||||||
 | 
					            configPaths.map(async (configPath) => {
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                const response = await fetch(configPath);
 | 
				
			||||||
 | 
					                if (response.ok) {
 | 
				
			||||||
 | 
					                    return response.json();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    throw new Error(`Failed to load config from ${configPath}`);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 创建一个对象来存储加载的配置
 | 
				
			||||||
 | 
					        const configData: Record<string, any> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 将文件名作为键,并赋值到 AppConfig 的对应静态属性
 | 
				
			||||||
 | 
					        configPaths.forEach((path, index) => {
 | 
				
			||||||
 | 
					            const fileName = path.split('/').pop()?.replace('.json', '');
 | 
				
			||||||
 | 
					            if (fileName) {
 | 
				
			||||||
 | 
					                console.log(fileName);
 | 
				
			||||||
 | 
					                (AppConfig as any)[fileName] = results[index]; // 设置到 AppConfig 静态属性
 | 
				
			||||||
 | 
					                configData[fileName] = results[index];          // 存储到 configData 对象
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 返回包含所有配置项的对象
 | 
				
			||||||
 | 
					        return configData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										111
									
								
								src/script/utils/Dictionary.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 泛型字典类
 | 
				
			||||||
 | 
					 * let dictionary = new Dictionary<string>(); // new一个对象
 | 
				
			||||||
 | 
					 * // 设置属性
 | 
				
			||||||
 | 
					 * dictionary.Set('gandalf', 'gandalf@email.com');
 | 
				
			||||||
 | 
					 * dictionary.Set('john', 'johnsnow@email.com');
 | 
				
			||||||
 | 
					 * dictionary.Set('tyrion', 'tyrion@email.com');
 | 
				
			||||||
 | 
					 * // 调用
 | 
				
			||||||
 | 
					 * console.log(dictionary.Size());
 | 
				
			||||||
 | 
					 * console.log(dictionary.Values());
 | 
				
			||||||
 | 
					 * console.log(dictionary.Get('tyrion'));
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export class Dictionary<T> {
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 字典项目
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private items: any = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 验证指定键是否在字典中
 | 
				
			||||||
 | 
					   * @param key 键
 | 
				
			||||||
 | 
					   * @returns 是否存在
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Has(key: string): boolean {
 | 
				
			||||||
 | 
					    return key in this.items
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 设置键值
 | 
				
			||||||
 | 
					   * @param key 键
 | 
				
			||||||
 | 
					   * @param value 值
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Set(key: string, value: T): void {
 | 
				
			||||||
 | 
					    this.items[key] = value
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 移除指定键
 | 
				
			||||||
 | 
					   * @param key 键
 | 
				
			||||||
 | 
					   * @returns 是否移除成功
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Remove(key: string): boolean {
 | 
				
			||||||
 | 
					    if (this.Has(key)) {
 | 
				
			||||||
 | 
					      delete this.items[key]
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 查找特定键的值
 | 
				
			||||||
 | 
					   * @param key 键
 | 
				
			||||||
 | 
					   * @returns 值
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Get(key: string): T {
 | 
				
			||||||
 | 
					    return this.Has(key) ? this.items[key] : undefined
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 获取字典所有的键
 | 
				
			||||||
 | 
					   * @returns 键数组
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Keys(): Array<string> {
 | 
				
			||||||
 | 
					    let values = new Array<string>() //存到数组中返回
 | 
				
			||||||
 | 
					    for (let k in this.items) {
 | 
				
			||||||
 | 
					      if (this.Has(k)) {
 | 
				
			||||||
 | 
					        values.push(k)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return values
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 获取字典所有的值
 | 
				
			||||||
 | 
					   * @returns 值数组
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Values(): Array<T> {
 | 
				
			||||||
 | 
					    // 存到数组中返回
 | 
				
			||||||
 | 
					    let values = new Array<T>()
 | 
				
			||||||
 | 
					    for (let k in this.items) {
 | 
				
			||||||
 | 
					      if (this.Has(k)) {
 | 
				
			||||||
 | 
					        values.push(this.items[k])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return values
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 获取所有键值
 | 
				
			||||||
 | 
					   * @returns 键值对对象
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public GetItems(): object {
 | 
				
			||||||
 | 
					    return this.items
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 清空字典
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Clear(): void {
 | 
				
			||||||
 | 
					    this.items = {}
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 获取字典大小
 | 
				
			||||||
 | 
					   * @returns
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Size(): number {
 | 
				
			||||||
 | 
					    return Object.keys(this.items).length
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										673
									
								
								src/script/utils/Dimensions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,673 @@
 | 
				
			|||||||
 | 
					import { Color3, Mesh, MeshBuilder, SpriteManager, Sprite, Vector3, Matrix, LinesMesh } from "@babylonjs/core";
 | 
				
			||||||
 | 
					import { Monobehiver } from "../base/Monobehiver";
 | 
				
			||||||
 | 
					import { AppConfig } from "@/script/public/AppConfig";
 | 
				
			||||||
 | 
					import { MainApp } from "@/script/public/MainApp";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Dimensions extends Monobehiver {
 | 
				
			||||||
 | 
					    private dimensionLines: (Mesh | LinesMesh)[] = [];
 | 
				
			||||||
 | 
					    private dimensionLabels: HTMLElement[] = [];
 | 
				
			||||||
 | 
					    private dimensionSprites: Sprite[] = [];
 | 
				
			||||||
 | 
					    private lineColor: Color3 = new Color3(0, 0.7, 1); // 默认尺寸线颜色 - 蓝色
 | 
				
			||||||
 | 
					    private labelColor: string = "#000000"; // 文字颜色
 | 
				
			||||||
 | 
					    private offset: number = 0.05; // 尺寸线与模型的偏移距离
 | 
				
			||||||
 | 
					    private scene: any;
 | 
				
			||||||
 | 
					    private enable: boolean = false;
 | 
				
			||||||
 | 
					    private dimensionsContainer: HTMLElement;
 | 
				
			||||||
 | 
					    private spriteManager!: SpriteManager;
 | 
				
			||||||
 | 
					    private animationSpeed: number = 0.05; // 动画速度
 | 
				
			||||||
 | 
					    private animating: boolean = false; // 是否正在动画中
 | 
				
			||||||
 | 
					    private animationFrames: Map<Mesh | LinesMesh, { start: Vector3, end: Vector3, progress: number, originalEnd: Vector3 }> = new Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(mainApp: MainApp) {
 | 
				
			||||||
 | 
					        super(mainApp);
 | 
				
			||||||
 | 
					        this.dimensionsContainer = document.createElement('div');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async Awake(): Promise<void> {
 | 
				
			||||||
 | 
					        this.scene = this.mainApp.appScene.object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 初始化精灵管理器
 | 
				
			||||||
 | 
					        this.spriteManager = new SpriteManager(
 | 
				
			||||||
 | 
					            'dimensionSpriteManager',
 | 
				
			||||||
 | 
					            '', // 空图片,因为我们不需要显示精灵本身
 | 
				
			||||||
 | 
					            10, // 最大精灵数量
 | 
				
			||||||
 | 
					            1,  // 精灵大小
 | 
				
			||||||
 | 
					            this.scene
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 创建尺寸标签容器
 | 
				
			||||||
 | 
					        this.dimensionsContainer.className = 'dimensions-container';
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.position = 'absolute';
 | 
				
			||||||
 | 
					        // this.dimensionsContainer.style.zIndex = '99';
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.top = '0';
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.left = '0';
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.width = '100%';
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.height = '100%';
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.pointerEvents = 'none';
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.userSelect = 'none';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加到DOM
 | 
				
			||||||
 | 
					        document.querySelector('.app-view')?.appendChild(this.dimensionsContainer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加场景渲染观察者,确保每帧更新标签位置
 | 
				
			||||||
 | 
					        this.scene.onBeforeRenderObservable.add(() => {
 | 
				
			||||||
 | 
					            this.updateDimensionsPosition();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        if (this.enable) {
 | 
				
			||||||
 | 
					            this.show()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            this.hide();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 生成并显示尺寸线和尺寸文本
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    show(): void {
 | 
				
			||||||
 | 
					        // 停止任何正在进行的动画
 | 
				
			||||||
 | 
					        this.stopAllAnimations();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 彻底清理所有内容
 | 
				
			||||||
 | 
					        this.clearDimensions();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 清理动画数据
 | 
				
			||||||
 | 
					        this.animationFrames.clear();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 创建新的尺寸线
 | 
				
			||||||
 | 
					        this.createDimensions();
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.display = 'block';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 隐藏所有尺寸线和尺寸文本
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hide(): void {
 | 
				
			||||||
 | 
					        // 先隐藏标签
 | 
				
			||||||
 | 
					        this.dimensionLabels.forEach(label => {
 | 
				
			||||||
 | 
					            label.style.transition = 'opacity 0.5s ease-in-out';
 | 
				
			||||||
 | 
					            label.style.opacity = '0';
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 启动隐藏动画
 | 
				
			||||||
 | 
					        this.startHideAnimation();
 | 
				
			||||||
 | 
					        this.dimensionsContainer.style.display = 'none';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 启动线条消失动画
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private startHideAnimation(): void {
 | 
				
			||||||
 | 
					        this.animating = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 在动画开始前清理所有箭头
 | 
				
			||||||
 | 
					        this.clearArrows();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 创建反向动画数据
 | 
				
			||||||
 | 
					        this.dimensionLines.forEach(line => {
 | 
				
			||||||
 | 
					            // 跳过箭头
 | 
				
			||||||
 | 
					            if (line.name.includes('arrow')) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 获取线条的起点和终点
 | 
				
			||||||
 | 
					            const points = line.getVerticesData('position');
 | 
				
			||||||
 | 
					            if (!points || points.length < 6) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const start = new Vector3(points[0], points[1], points[2]);
 | 
				
			||||||
 | 
					            const end = new Vector3(points[3], points[4], points[5]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 存储动画信息 - 注意这里与创建动画相反
 | 
				
			||||||
 | 
					            this.animationFrames.set(line, {
 | 
				
			||||||
 | 
					                start: start.clone(),
 | 
				
			||||||
 | 
					                end: start.clone(),  // 终点变为起点(线条会收缩)
 | 
				
			||||||
 | 
					                progress: 0,
 | 
				
			||||||
 | 
					                originalEnd: end.clone() // 存储原始终点
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加动画更   this.photoDome_yundong.mesh.renderingGroupId = 1;新观察者
 | 
				
			||||||
 | 
					        const observer = this.scene.onBeforeRenderObservable.add(() => {
 | 
				
			||||||
 | 
					            let allCompleted = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 更新每条线的动画
 | 
				
			||||||
 | 
					            this.animationFrames.forEach((data, line) => {
 | 
				
			||||||
 | 
					                if (data.progress < 1) {
 | 
				
			||||||
 | 
					                    // 更新进度
 | 
				
			||||||
 | 
					                    data.progress += this.animationSpeed;
 | 
				
			||||||
 | 
					                    if (data.progress > 1) data.progress = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // 计算当前终点(收缩效果)
 | 
				
			||||||
 | 
					                    const currentEnd = new Vector3(
 | 
				
			||||||
 | 
					                        data.originalEnd.x - (data.originalEnd.x - data.end.x) * data.progress,
 | 
				
			||||||
 | 
					                        data.originalEnd.y - (data.originalEnd.y - data.end.y) * data.progress,
 | 
				
			||||||
 | 
					                        data.originalEnd.z - (data.originalEnd.z - data.end.z) * data.progress
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // 更新线条
 | 
				
			||||||
 | 
					                    const points = [data.start, currentEnd];
 | 
				
			||||||
 | 
					                    if (line instanceof LinesMesh) {
 | 
				
			||||||
 | 
					                        MeshBuilder.CreateLines(line.name, { points, instance: line });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    allCompleted = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 移除箭头
 | 
				
			||||||
 | 
					            this.dimensionLines.forEach(line => {
 | 
				
			||||||
 | 
					                if (line.name.includes('arrow')) {
 | 
				
			||||||
 | 
					                    line.dispose();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 如果所有线条动画都完成了
 | 
				
			||||||
 | 
					            if (allCompleted) {
 | 
				
			||||||
 | 
					                // 清除所有内容
 | 
				
			||||||
 | 
					                this.clearDimensions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 移除观察者
 | 
				
			||||||
 | 
					                this.scene.onBeforeRenderObservable.remove(observer);
 | 
				
			||||||
 | 
					                this.animating = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 清除所有尺寸线和尺寸文本
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private clearDimensions(): void {
 | 
				
			||||||
 | 
					        // 停止任何正在进行的动画
 | 
				
			||||||
 | 
					        this.animating = false;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 清除尺寸线(包括箭头)
 | 
				
			||||||
 | 
					        this.dimensionLines.forEach(line => {
 | 
				
			||||||
 | 
					            if (line) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    line.dispose();
 | 
				
			||||||
 | 
					                } catch (error) {
 | 
				
			||||||
 | 
					                    console.warn('清理线段时发生错误:', error);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.dimensionLines = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 清除尺寸精灵
 | 
				
			||||||
 | 
					        this.dimensionSprites.forEach(sprite => {
 | 
				
			||||||
 | 
					            if (sprite) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    sprite.dispose();
 | 
				
			||||||
 | 
					                } catch (error) {
 | 
				
			||||||
 | 
					                    console.warn('清理精灵时发生错误:', error);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.dimensionSprites = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 清除尺寸标签
 | 
				
			||||||
 | 
					        this.dimensionLabels.forEach(label => {
 | 
				
			||||||
 | 
					            if (label && label.parentNode) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    label.parentNode.removeChild(label);
 | 
				
			||||||
 | 
					                } catch (error) {
 | 
				
			||||||
 | 
					                    console.warn('清理标签时发生错误:', error);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.dimensionLabels = [];
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 清理动画数据
 | 
				
			||||||
 | 
					        this.animationFrames.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    left: boolean = false;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据包围盒信息创建尺寸线和文字标注
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private createDimensions(): void {
 | 
				
			||||||
 | 
					        if (!this.mainApp.boxHelper) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 得到包围盒的坐标
 | 
				
			||||||
 | 
					        const boundingInfo = this.mainApp.boxHelper.parent.getBoundingInfo();
 | 
				
			||||||
 | 
					        const min = boundingInfo.boundingBox.minimumWorld;
 | 
				
			||||||
 | 
					        const max = boundingInfo.boundingBox.maximumWorld;
 | 
				
			||||||
 | 
					        console.log(AppConfig.Scene.dimensions);
 | 
				
			||||||
 | 
					        const {x,y,z} = AppConfig.Scene.dimensions;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        //放在右侧
 | 
				
			||||||
 | 
					        if (!this.left) {
 | 
				
			||||||
 | 
					            // 创建宽度标注(X轴方向)- 放到前面
 | 
				
			||||||
 | 
					            this.createDimensionLine(
 | 
				
			||||||
 | 
					                new Vector3(min.x, min.y, min.z - this.offset),
 | 
				
			||||||
 | 
					                new Vector3(max.x, min.y, min.z - this.offset),
 | 
				
			||||||
 | 
					                z + ' mm',
 | 
				
			||||||
 | 
					                0
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 创建高度标注(Y轴方向)- 放到前右
 | 
				
			||||||
 | 
					            this.createDimensionLine(
 | 
				
			||||||
 | 
					                new Vector3(max.x , min.y, max.z+this.offset),
 | 
				
			||||||
 | 
					                new Vector3(max.x , max.y, max.z+ this.offset),
 | 
				
			||||||
 | 
					                x + ' mm',
 | 
				
			||||||
 | 
					                1
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 创建深度标注(Z轴方向)- 放到右侧
 | 
				
			||||||
 | 
					            this.createDimensionLine(
 | 
				
			||||||
 | 
					                new Vector3(max.x - this.offset, min.y, min.z),
 | 
				
			||||||
 | 
					                new Vector3(max.x - this.offset, min.y, max.z),
 | 
				
			||||||
 | 
					                x + ' mm',
 | 
				
			||||||
 | 
					                2
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            //放在左侧
 | 
				
			||||||
 | 
					            // 创建宽度标注(X轴方向)- 放到后面
 | 
				
			||||||
 | 
					            this.createDimensionLine(
 | 
				
			||||||
 | 
					                new Vector3(min.x, min.y, max.z + this.offset),
 | 
				
			||||||
 | 
					                new Vector3(max.x, min.y, max.z + this.offset),
 | 
				
			||||||
 | 
					                5860 + ' mm',
 | 
				
			||||||
 | 
					                0
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 创建高度标注(Y轴方向)- 放到前右
 | 
				
			||||||
 | 
					            this.createDimensionLine(
 | 
				
			||||||
 | 
					                new Vector3(max.x - this.offset, min.y, min.z),
 | 
				
			||||||
 | 
					                new Vector3(max.x - this.offset, max.y, min.z),
 | 
				
			||||||
 | 
					                1960 + ' mm',
 | 
				
			||||||
 | 
					                1
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 创建深度标注(Z轴方向)- 放到右侧
 | 
				
			||||||
 | 
					            this.createDimensionLine(
 | 
				
			||||||
 | 
					                new Vector3(max.x - this.offset, min.y, min.z),
 | 
				
			||||||
 | 
					                new Vector3(max.x - this.offset, min.y, max.z),
 | 
				
			||||||
 | 
					                1910 + ' mm',
 | 
				
			||||||
 | 
					                2
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 创建一条尺寸线和对应的文字标注
 | 
				
			||||||
 | 
					     * @param start 起点位置
 | 
				
			||||||
 | 
					     * @param end 终点位置
 | 
				
			||||||
 | 
					     * @param text 显示的文本
 | 
				
			||||||
 | 
					     * @param dimensionType 维度类型(0=宽度,1=高度,2=深度)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private createDimensionLine(start: Vector3, end: Vector3, text: string, dimensionType: number): void {
 | 
				
			||||||
 | 
					        // 创建初始线段(只有起点)
 | 
				
			||||||
 | 
					        const initialPoints = [start, start.clone()];
 | 
				
			||||||
 | 
					        const line = MeshBuilder.CreateLines(`dimension_line_${dimensionType}`, {
 | 
				
			||||||
 | 
					            points: initialPoints,
 | 
				
			||||||
 | 
					            updatable: true
 | 
				
			||||||
 | 
					        }, this.scene);
 | 
				
			||||||
 | 
					        line.color = this.lineColor;
 | 
				
			||||||
 | 
					        line.isPickable = false;
 | 
				
			||||||
 | 
					        line.renderingGroupId =1
 | 
				
			||||||
 | 
					        // 存储动画信息
 | 
				
			||||||
 | 
					        this.animationFrames.set(line, {
 | 
				
			||||||
 | 
					            start: start.clone(),
 | 
				
			||||||
 | 
					            end: end.clone(),
 | 
				
			||||||
 | 
					            progress: 0,
 | 
				
			||||||
 | 
					            originalEnd: end.clone()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 计算文本位置(线段中点)
 | 
				
			||||||
 | 
					        const textPosition = new Vector3(
 | 
				
			||||||
 | 
					            (start.x + end.x) / 2,
 | 
				
			||||||
 | 
					            (start.y + end.y) / 2,
 | 
				
			||||||
 | 
					            (start.z + end.z) / 2
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 创建精灵用于位置跟踪(但初始不显示)
 | 
				
			||||||
 | 
					        const sprite = new Sprite(`dimension_sprite_${dimensionType}`, this.spriteManager);
 | 
				
			||||||
 | 
					        sprite.position = textPosition;
 | 
				
			||||||
 | 
					        sprite.size = 0;
 | 
				
			||||||
 | 
					        sprite.isPickable = false;
 | 
				
			||||||
 | 
					        this.dimensionSprites.push(sprite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 创建HTML文本标签(初始不显示)
 | 
				
			||||||
 | 
					        const label = this.createDimensionLabel(text, dimensionType);
 | 
				
			||||||
 | 
					        label.style.opacity = '0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加到数组中便于后续管理
 | 
				
			||||||
 | 
					        this.dimensionLines.push(line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 启动动画(如果没有在进行)
 | 
				
			||||||
 | 
					        if (!this.animating) {
 | 
				
			||||||
 | 
					            this.startAnimation();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 启动线条生成动画
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private startAnimation(): void {
 | 
				
			||||||
 | 
					        this.animating = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 在动画开始前清理所有箭头
 | 
				
			||||||
 | 
					        this.clearArrows();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加动画更新观察者
 | 
				
			||||||
 | 
					        const observer = this.scene.onBeforeRenderObservable.add(() => {
 | 
				
			||||||
 | 
					            let allCompleted = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 更新每条线的动画
 | 
				
			||||||
 | 
					            this.animationFrames.forEach((data, line) => {
 | 
				
			||||||
 | 
					                if (data.progress < 1) {
 | 
				
			||||||
 | 
					                    // 更新进度
 | 
				
			||||||
 | 
					                    data.progress += this.animationSpeed;
 | 
				
			||||||
 | 
					                    if (data.progress > 1) data.progress = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // 计算当前终点
 | 
				
			||||||
 | 
					                    const currentEnd = new Vector3(
 | 
				
			||||||
 | 
					                        data.start.x + (data.end.x - data.start.x) * data.progress,
 | 
				
			||||||
 | 
					                        data.start.y + (data.end.y - data.start.y) * data.progress,
 | 
				
			||||||
 | 
					                        data.start.z + (data.end.z - data.start.z) * data.progress
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // 更新线条
 | 
				
			||||||
 | 
					                    const points = [data.start, currentEnd];
 | 
				
			||||||
 | 
					                    if (line instanceof LinesMesh) {
 | 
				
			||||||
 | 
					                        MeshBuilder.CreateLines(line.name, { points, instance: line });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    allCompleted = false;
 | 
				
			||||||
 | 
					                } else if (data.progress === 1 && !line.metadata?.arrowsCreated) {
 | 
				
			||||||
 | 
					                    // 线条完成后创建箭头
 | 
				
			||||||
 | 
					                    const index = parseInt(line.name.split('_').pop() || '0');
 | 
				
			||||||
 | 
					                    this.addArrowHeads(data.start, data.end, index);
 | 
				
			||||||
 | 
					                    line.metadata = { arrowsCreated: true };
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 如果所有线条动画都完成了
 | 
				
			||||||
 | 
					            if (allCompleted) {
 | 
				
			||||||
 | 
					                // 显示标签(淡入效果)
 | 
				
			||||||
 | 
					                this.dimensionLabels.forEach(label => {
 | 
				
			||||||
 | 
					                    label.style.transition = 'opacity 0.5s ease-in-out';
 | 
				
			||||||
 | 
					                    label.style.opacity = '1';
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 移除观察者
 | 
				
			||||||
 | 
					                this.scene.onBeforeRenderObservable.remove(observer);
 | 
				
			||||||
 | 
					                this.animating = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 添加箭头头部
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private addArrowHeads(start: Vector3, end: Vector3, dimensionType: number): void {
 | 
				
			||||||
 | 
					        // 计算方向向量
 | 
				
			||||||
 | 
					        const direction = end.subtract(start).normalize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 线段尺寸
 | 
				
			||||||
 | 
					        const lineLength = 0.03; // 垂直线段的长度
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 计算垂直于方向的向量,用于创建垂直线段
 | 
				
			||||||
 | 
					        let perpendicular: Vector3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 根据不同维度选择合适的垂直向量
 | 
				
			||||||
 | 
					        switch (dimensionType) {
 | 
				
			||||||
 | 
					            case 0: // X轴(宽度)
 | 
				
			||||||
 | 
					                perpendicular = new Vector3(0, lineLength, 0);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 1: // Y轴(高度)
 | 
				
			||||||
 | 
					                perpendicular = new Vector3(0, 0, lineLength);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 2: // Z轴(深度)
 | 
				
			||||||
 | 
					                perpendicular = new Vector3(0, lineLength, 0);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                perpendicular = new Vector3(0, lineLength, 0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 创建垂直线段(两端各一个)
 | 
				
			||||||
 | 
					        // 起点的垂直线段
 | 
				
			||||||
 | 
					        const startLinePoints = [
 | 
				
			||||||
 | 
					            start.add(perpendicular),
 | 
				
			||||||
 | 
					            start.subtract(perpendicular)
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const startLine = MeshBuilder.CreateLines(`dimension_arrow_start_${dimensionType}`, {
 | 
				
			||||||
 | 
					            points: startLinePoints
 | 
				
			||||||
 | 
					        }, this.scene);
 | 
				
			||||||
 | 
					        startLine.color = this.lineColor;
 | 
				
			||||||
 | 
					        startLine.isPickable = false;
 | 
				
			||||||
 | 
					        startLine.renderingGroupId = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 终点的垂直线段
 | 
				
			||||||
 | 
					        const endLinePoints = [
 | 
				
			||||||
 | 
					            end.add(perpendicular),
 | 
				
			||||||
 | 
					            end.subtract(perpendicular)
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const endLine = MeshBuilder.CreateLines(`dimension_arrow_end_${dimensionType}`, {
 | 
				
			||||||
 | 
					            points: endLinePoints
 | 
				
			||||||
 | 
					        }, this.scene);
 | 
				
			||||||
 | 
					        endLine.color = this.lineColor;
 | 
				
			||||||
 | 
					        endLine.isPickable = false;
 | 
				
			||||||
 | 
					        endLine.renderingGroupId = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加到数组中
 | 
				
			||||||
 | 
					        this.dimensionLines.push(startLine, endLine);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 创建HTML文本标签
 | 
				
			||||||
 | 
					     * @param text 显示的文本
 | 
				
			||||||
 | 
					     * @param dimensionType 维度类型
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private createDimensionLabel(text: string, dimensionType: number): HTMLElement {
 | 
				
			||||||
 | 
					        // 创建标签元素
 | 
				
			||||||
 | 
					        const label = document.createElement('div');
 | 
				
			||||||
 | 
					        label.className = 'dimension-label';
 | 
				
			||||||
 | 
					        label.id = `dimension_label_${dimensionType}`;
 | 
				
			||||||
 | 
					        label.textContent = text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 设置标签样式
 | 
				
			||||||
 | 
					        label.style.position = 'absolute';
 | 
				
			||||||
 | 
					        label.style.color = this.labelColor;
 | 
				
			||||||
 | 
					        label.style.backgroundColor = 'rgb(255, 255, 255)'; // 浅灰色背景
 | 
				
			||||||
 | 
					        label.style.padding = '4px 8px';
 | 
				
			||||||
 | 
					        label.style.borderRadius = '4px';
 | 
				
			||||||
 | 
					        label.style.fontSize = '14px';
 | 
				
			||||||
 | 
					        label.style.fontWeight = 'bold';
 | 
				
			||||||
 | 
					        label.style.transform = 'translate(-50%, -50%)';
 | 
				
			||||||
 | 
					        label.style.whiteSpace = 'nowrap';
 | 
				
			||||||
 | 
					        label.style.pointerEvents = 'none';
 | 
				
			||||||
 | 
					        label.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)'; // 添加阴影效果
 | 
				
			||||||
 | 
					        label.style.border = '1px solid rgba(200, 200, 200, 0.5)'; // 添加浅灰色边框
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加文字阴影提升可读性
 | 
				
			||||||
 | 
					        label.style.textShadow = `
 | 
				
			||||||
 | 
					            0 0 2px rgba(255, 255, 255, 0.5),
 | 
				
			||||||
 | 
					            0 0 4px rgba(0, 0, 0, 0.2)
 | 
				
			||||||
 | 
					        `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 添加到容器
 | 
				
			||||||
 | 
					        this.dimensionsContainer.appendChild(label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 保存引用
 | 
				
			||||||
 | 
					        this.dimensionLabels.push(label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return label;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 更新所有尺寸标签的位置和可见性
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private updateDimensionsPosition(): void {
 | 
				
			||||||
 | 
					        if (this.dimensionSprites.length === 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            for (let i = 0; i < this.dimensionSprites.length; i++) {
 | 
				
			||||||
 | 
					                const sprite = this.dimensionSprites[i];
 | 
				
			||||||
 | 
					                const label = this.dimensionLabels[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!sprite || !label) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 使用worldToScreen将3D位置转换为屏幕坐标
 | 
				
			||||||
 | 
					                const screenPos = this.worldToScreen(
 | 
				
			||||||
 | 
					                    sprite.position,
 | 
				
			||||||
 | 
					                    this.mainApp.appCamera.object,
 | 
				
			||||||
 | 
					                    this.mainApp.appScene.object,
 | 
				
			||||||
 | 
					                    this.mainApp.appEngin.object
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 计算是否在视线内
 | 
				
			||||||
 | 
					                this.updateAnnotationOpacity(sprite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 更新标签位置
 | 
				
			||||||
 | 
					                label.style.left = screenPos.x / 2 + 'px';
 | 
				
			||||||
 | 
					                label.style.top = screenPos.y / 2 + 'px';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 同步标签的可见性与sprite的可见性
 | 
				
			||||||
 | 
					                label.style.display = sprite.isVisible ? 'block' : 'none';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            console.error("更新尺寸标签位置时发生错误:", error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    spriteBehindObject: boolean = false;
 | 
				
			||||||
 | 
					    updateAnnotationOpacity(sprite: Sprite) {
 | 
				
			||||||
 | 
					        this.spriteBehindObject =
 | 
				
			||||||
 | 
					            this.distance(this.mainApp.appCamera.object.position, sprite.position) >
 | 
				
			||||||
 | 
					            this.distance(this.mainApp.appCamera.object.position, Vector3.Zero())
 | 
				
			||||||
 | 
					        sprite.size = this.spriteBehindObject ? 0 : 1
 | 
				
			||||||
 | 
					        sprite.isVisible = !this.spriteBehindObject
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    distance(v1: Vector3, v2: Vector3) {
 | 
				
			||||||
 | 
					        let x = Math.pow(v1.x - v2.x, 2)
 | 
				
			||||||
 | 
					        let y = Math.pow(v1.y - v2.y, 2)
 | 
				
			||||||
 | 
					        let z = Math.pow(v1.z - v2.z, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let distance = Math.sqrt(x + y + z)
 | 
				
			||||||
 | 
					        return distance
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将世界坐标转换为屏幕坐标
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private worldToScreen(point: Vector3, camera: any, scene: any, engine: any): Vector3 {
 | 
				
			||||||
 | 
					        return Vector3.Project(
 | 
				
			||||||
 | 
					            point,
 | 
				
			||||||
 | 
					            Matrix.Identity(),
 | 
				
			||||||
 | 
					            scene.getTransformMatrix(),
 | 
				
			||||||
 | 
					            camera.viewport.toGlobal(
 | 
				
			||||||
 | 
					                engine.getRenderWidth(),
 | 
				
			||||||
 | 
					                engine.getRenderHeight()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 更新尺寸线和文字位置(当模型尺寸或位置变化时调用)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    update(): void {
 | 
				
			||||||
 | 
					        if (!this.enable) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 否则只更新标签位置
 | 
				
			||||||
 | 
					        this.updateDimensionsPosition();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 切换尺寸线的可见性
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    // 定义一个名为 toggle 的方法,接收一个布尔类型的参数 visible
 | 
				
			||||||
 | 
					    toggle(visible: boolean): void {
 | 
				
			||||||
 | 
					        // 如果正在动画中,先停止所有动画
 | 
				
			||||||
 | 
					        if (this.animating) {
 | 
				
			||||||
 | 
					            this.stopAllAnimations();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 将传入的 visible 参数赋值给实例的 enable 属性
 | 
				
			||||||
 | 
					        this.enable = visible;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 判断 enable 属性的值
 | 
				
			||||||
 | 
					        if (this.enable) {
 | 
				
			||||||
 | 
					            // 如果 enable 为 true,则调用 show 方法
 | 
				
			||||||
 | 
					            this.show();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // 如果 enable 为 false,则调用 hide 方法
 | 
				
			||||||
 | 
					            this.hide();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 清理资源
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    clean(): void {
 | 
				
			||||||
 | 
					        // 移除场景渲染观察者
 | 
				
			||||||
 | 
					        if (this.scene && this.scene.onBeforeRenderObservable) {
 | 
				
			||||||
 | 
					            this.scene.onBeforeRenderObservable.clear?.();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.clearDimensions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 销毁精灵管理器
 | 
				
			||||||
 | 
					        if (this.spriteManager) {
 | 
				
			||||||
 | 
					            this.spriteManager.dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 移除容器
 | 
				
			||||||
 | 
					        if (this.dimensionsContainer && this.dimensionsContainer.parentNode) {
 | 
				
			||||||
 | 
					            this.dimensionsContainer.parentNode.removeChild(this.dimensionsContainer);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 窗口大小改变时调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    onResize(): void {
 | 
				
			||||||
 | 
					        if (this.enable) {
 | 
				
			||||||
 | 
					            // 窗口大小改变时只需更新标签位置
 | 
				
			||||||
 | 
					            this.updateDimensionsPosition();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 清理所有箭头
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private clearArrows(): void {
 | 
				
			||||||
 | 
					        this.dimensionLines.forEach(line => {
 | 
				
			||||||
 | 
					            if (line.name.includes('arrow')) {
 | 
				
			||||||
 | 
					                line.dispose();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.dimensionLines = this.dimensionLines.filter(line => !line.name.includes('arrow'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 停止所有正在进行的动画
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private stopAllAnimations(): void {
 | 
				
			||||||
 | 
					        // 停止所有正在进行的动画
 | 
				
			||||||
 | 
					        this.animating = false;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 安全地清理观察者
 | 
				
			||||||
 | 
					        if (this.scene && this.scene.onBeforeRenderObservable) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                // 尝试移除所有与尺寸相关的观察者
 | 
				
			||||||
 | 
					                this.scene.onBeforeRenderObservable.clear();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // 重新添加位置更新观察者
 | 
				
			||||||
 | 
					                this.scene.onBeforeRenderObservable.add(() => {
 | 
				
			||||||
 | 
					                    this.updateDimensionsPosition();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            } catch (error) {
 | 
				
			||||||
 | 
					                console.warn('清理动画观察者时发生错误:', error);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 清理动画数据
 | 
				
			||||||
 | 
					        this.animationFrames.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/script/utils/ForMatTime.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					export const ForMatTime = (time: number) => {
 | 
				
			||||||
 | 
					  const millis = parseInt((time % 1000).toString())
 | 
				
			||||||
 | 
					  let second = 0
 | 
				
			||||||
 | 
					  let totalMinute = 0
 | 
				
			||||||
 | 
					  let minute = 0
 | 
				
			||||||
 | 
					  let hour = 0
 | 
				
			||||||
 | 
					  let result = ''
 | 
				
			||||||
 | 
					  const totalSecond = parseInt((time / 1000).toString())
 | 
				
			||||||
 | 
					  if (totalSecond > 59) {
 | 
				
			||||||
 | 
					    // 总秒数大于59 需要计算总分钟 数
 | 
				
			||||||
 | 
					    second = parseInt((totalSecond % 60).toString())
 | 
				
			||||||
 | 
					    totalMinute = parseInt((totalSecond / 60).toString())
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    second = totalSecond
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (totalMinute > 59) {
 | 
				
			||||||
 | 
					    minute = parseInt((totalMinute % 60).toString())
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    minute = totalMinute
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (minute < 10) {
 | 
				
			||||||
 | 
					    result += '0' + minute
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    result += '' + minute
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (second < 10) {
 | 
				
			||||||
 | 
					    result += ':0' + second
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    result += ':' + second
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (millis < 10) {
 | 
				
			||||||
 | 
					    result += ':00' + millis
 | 
				
			||||||
 | 
					  } else if (millis < 100) {
 | 
				
			||||||
 | 
					    result += ':0' + millis
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    result += ':' + millis
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||