SyuanYu 8 months ago
commit
3095c0d054
53 changed files with 9050 additions and 0 deletions
  1. 30 0
      .gitignore
  2. 3 0
      .vscode/extensions.json
  3. 29 0
      README.md
  4. 25 0
      index.html
  5. 8 0
      jsconfig.json
  6. 3536 0
      package-lock.json
  7. 31 0
      package.json
  8. BIN
      public/favicon.png
  9. 28 0
      src/App.vue
  10. 23 0
      src/assets/base.css
  11. 118 0
      src/assets/img/angles-up-solid.svg
  12. BIN
      src/assets/img/banner-1.jpg
  13. BIN
      src/assets/img/banner-2.jpg
  14. BIN
      src/assets/img/banner.jpg
  15. BIN
      src/assets/img/icon/素材-01.png
  16. BIN
      src/assets/img/icon/素材-02.png
  17. BIN
      src/assets/img/icon/素材-03.png
  18. BIN
      src/assets/img/icon/素材-04.png
  19. BIN
      src/assets/img/icon/素材-05.png
  20. BIN
      src/assets/img/icon/素材-06.png
  21. BIN
      src/assets/img/icon/素材-07.png
  22. BIN
      src/assets/img/icon/素材-08.png
  23. BIN
      src/assets/img/icon/素材-09.png
  24. BIN
      src/assets/img/icon/素材-10.png
  25. BIN
      src/assets/img/icon/素材-15.png
  26. 1 0
      src/assets/img/location-dot-solid.svg
  27. 51 0
      src/assets/img/logo.svg
  28. 0 0
      src/assets/img/map/b1.svg
  29. 1 0
      src/assets/img/paper-plane-solid.svg
  30. BIN
      src/assets/img/pause-button.png
  31. 1 0
      src/assets/img/phone-solid.svg
  32. BIN
      src/assets/img/play-button.png
  33. 1 0
      src/assets/logo.svg
  34. 1 0
      src/assets/main.css
  35. BIN
      src/assets/video/cupola_000090.mp4
  36. BIN
      src/assets/video/start.mp4
  37. BIN
      src/assets/video/start_1.mp4
  38. BIN
      src/assets/video/路線24-26-27-v3.mp4
  39. 23 0
      src/components/ArGuided.vue
  40. 4288 0
      src/components/Chat.vue
  41. 27 0
      src/components/Navbar.vue
  42. 128 0
      src/language/en.json
  43. 128 0
      src/language/ja.json
  44. 128 0
      src/language/ko.json
  45. 128 0
      src/language/zh.json
  46. 30 0
      src/main.js
  47. 20 0
      src/plugins/i18n.js
  48. 28 0
      src/plugins/vuetify.js
  49. 62 0
      src/router/index.js
  50. 12 0
      src/stores/counter.js
  51. 119 0
      src/views/ArTourView.vue
  52. 26 0
      src/views/HomeView.vue
  53. 16 0
      vite.config.js

+ 30 - 0
.gitignore

@@ -0,0 +1,30 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo

+ 3 - 0
.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
+}

+ 29 - 0
README.md

@@ -0,0 +1,29 @@
+# ai-chatbot  
+
+This template should help get you started developing with Vue 3 in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vitejs.dev/config/).
+
+## Project Setup
+
+```sh
+npm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+npm run dev
+```
+
+### Compile and Minify for Production
+
+```sh
+npm run build
+```

+ 25 - 0
index.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <link rel="icon" href="/favicon.png">
+  <!-- Google Fonts -->
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100..900&display=swap" rel="stylesheet">
+
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <!-- 移除手機版縮放功能 -->
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+  <title>台北101 AI智能客服</title>
+</head>
+
+<body>
+  <div id="app"></div>
+  <script type="module" src="/src/main.js"></script>
+  <script src="https://unpkg.com/vuejs-vr@latest/dist/vue-vr.min.js"></script>
+
+</body>
+
+</html>

+ 8 - 0
jsconfig.json

@@ -0,0 +1,8 @@
+{
+  "compilerOptions": {
+    "paths": {
+      "@/*": ["./src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"]
+}

+ 3536 - 0
package-lock.json

@@ -0,0 +1,3536 @@
+{
+  "name": "ai-chatbot",
+  "version": "0.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "ai-chatbot",
+      "version": "0.0.0",
+      "dependencies": {
+        "aframe": "^1.5.0",
+        "animate.css": "^4.1.1",
+        "ar.js": "^2.2.2",
+        "axios": "^1.6.7",
+        "pinia": "^2.1.7",
+        "recorder-core": "^1.3.24040900",
+        "swiper": "^11.0.7",
+        "vue": "^3.3.11",
+        "vue-i18n": "^9.11.0",
+        "vue-qrcode-reader": "^5.5.6",
+        "vue-router": "^4.2.5",
+        "vuetify": "^3.5.14"
+      },
+      "devDependencies": {
+        "@mdi/font": "^7.4.47",
+        "@vitejs/plugin-vue": "^4.5.2",
+        "sass": "^1.70.0",
+        "vite": "^5.0.10"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.23.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz",
+      "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==",
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
+      "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
+      "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
+      "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
+      "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
+      "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
+      "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
+      "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
+      "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
+      "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
+      "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
+      "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
+      "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
+      "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
+      "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
+      "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
+      "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
+      "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
+      "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
+      "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
+      "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
+      "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@intlify/core-base": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.11.0.tgz",
+      "integrity": "sha512-cveOqAstjLZIiyatcP/HrzrQ87cZI8ScPQna3yvoM8zjcjcIRK1MRvmxUNlPdg0rTNJMZw7rixPVM58O5aHVPA==",
+      "dependencies": {
+        "@intlify/message-compiler": "9.11.0",
+        "@intlify/shared": "9.11.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.11.0.tgz",
+      "integrity": "sha512-x31Gl7cscnoI4UUY1yaIy8e7vVMVW1VVlTXZz4SIHKqoSEUkfmgqK8NAx1e7RcoHEbICR7uyCbud0ZL1s4OGXQ==",
+      "dependencies": {
+        "@intlify/shared": "9.11.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.11.0.tgz",
+      "integrity": "sha512-KHSNgi7sRjmSm7aD8QH8WFt9VfKaekJuJ473opbJlkGY3EDnDUU8ikIhG8PbasQbgNvbY3m3tWNGqk2omIdwMA==",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.15",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+    },
+    "node_modules/@mdi/font": {
+      "version": "7.4.47",
+      "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz",
+      "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==",
+      "dev": true
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz",
+      "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz",
+      "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz",
+      "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz",
+      "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz",
+      "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz",
+      "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz",
+      "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz",
+      "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz",
+      "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz",
+      "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz",
+      "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz",
+      "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz",
+      "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@sindresorhus/is": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
+      "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@szmarczak/http-timer": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
+      "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+      "dependencies": {
+        "defer-to-connect": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@types/dom-webcodecs": {
+      "version": "0.1.11",
+      "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.11.tgz",
+      "integrity": "sha512-yPEZ3z7EohrmOxbk/QTAa0yonMFkNkjnVXqbGb7D4rMr+F1dGQ8ZUFxXkyLLJuiICPejZ0AZE9Rrk9wUCczx4A=="
+    },
+    "node_modules/@types/emscripten": {
+      "version": "1.39.13",
+      "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.13.tgz",
+      "integrity": "sha512-cFq+fO/isvhvmuP/+Sl4K4jtU6E23DoivtbO4r50e3odaxAiVdbfSYRDdJ4gCdxx+3aRjhphS5ZMwIH4hFy/Cw=="
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
+    "node_modules/@ungap/custom-elements": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@ungap/custom-elements/-/custom-elements-1.3.0.tgz",
+      "integrity": "sha512-f4q/s76+8nOy+fhrNHyetuoPDR01lmlZB5czfCG+OOnBw/Wf+x48DcCDPmMQY7oL8xYFL8qfenMoiS8DUkKBUw=="
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+      "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
+      "dev": true,
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.0.0 || ^5.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz",
+      "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==",
+      "dependencies": {
+        "@babel/parser": "^7.23.6",
+        "@vue/shared": "3.4.15",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz",
+      "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==",
+      "dependencies": {
+        "@vue/compiler-core": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz",
+      "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==",
+      "dependencies": {
+        "@babel/parser": "^7.23.6",
+        "@vue/compiler-core": "3.4.15",
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/compiler-ssr": "3.4.15",
+        "@vue/shared": "3.4.15",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.5",
+        "postcss": "^8.4.33",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz",
+      "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
+      "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz",
+      "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==",
+      "dependencies": {
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz",
+      "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==",
+      "dependencies": {
+        "@vue/reactivity": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz",
+      "integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==",
+      "dependencies": {
+        "@vue/runtime-core": "3.4.15",
+        "@vue/shared": "3.4.15",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz",
+      "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.4.15",
+        "@vue/shared": "3.4.15"
+      },
+      "peerDependencies": {
+        "vue": "3.4.15"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
+      "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g=="
+    },
+    "node_modules/aframe": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/aframe/-/aframe-1.5.0.tgz",
+      "integrity": "sha512-QvxxF2jbQbSDnPouPveSm93OxfFUu5V6UmQoLbbf207BR/o4bM3trpMDCoGiYE2oQCe0d/VG56OyFmYDwYDfBg==",
+      "dependencies": {
+        "@ungap/custom-elements": "^1.1.0",
+        "buffer": "^6.0.3",
+        "custom-event-polyfill": "^1.0.6",
+        "debug": "github:ngokevin/debug#noTimestamp",
+        "deep-assign": "^2.0.0",
+        "load-bmfont": "^1.2.3",
+        "object-assign": "^4.0.1",
+        "present": "0.0.6",
+        "promise-polyfill": "^3.1.0",
+        "super-animejs": "^3.1.0",
+        "super-three": "0.158.0",
+        "three-bmfont-text": "github:dmarcos/three-bmfont-text#eed4878795be9b3e38cf6aec6b903f56acd1f695",
+        "webvr-polyfill": "^0.10.12"
+      },
+      "engines": {
+        "node": ">= 4.6.0",
+        "npm": ">= 2.15.9"
+      }
+    },
+    "node_modules/an-array": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/an-array/-/an-array-1.0.0.tgz",
+      "integrity": "sha512-M175GYI7RmsYu24Ok383yZQa3eveDfNnmhTe3OQ3bm70bEovz2gWenH+ST/n32M8lrwLWk74hcPds5CDRPe2wg=="
+    },
+    "node_modules/animate.css": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz",
+      "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/ar.js": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/ar.js/-/ar.js-2.2.2.tgz",
+      "integrity": "sha512-ThNE9oOGcosmMRKVkkKgHgHhDpLKXikLkFGCviAka5L3GAP3mpU1S5xJ9UWviJDncfzDfyezDCGe+bm321nyqQ=="
+    },
+    "node_modules/array-shuffle": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/array-shuffle/-/array-shuffle-1.0.1.tgz",
+      "integrity": "sha512-PBqgo1Y2XWSksBzq3GFPEb798ZrW2snAcmr4drbVeF/6MT/5aBlkGJEvu5A/CzXHf4EjbHOj/ZowatjlIiVidA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/as-number": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/as-number/-/as-number-1.0.0.tgz",
+      "integrity": "sha512-HkI/zLo2AbSRO4fqVkmyf3hms0bJDs3iboHqTrNuwTiCRvdYXM7HFhfhB6Dk51anV2LM/IMB83mtK9mHw4FlAg=="
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.6.7",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
+      "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
+      "dependencies": {
+        "follow-redirects": "^1.15.4",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/barcode-detector": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/barcode-detector/-/barcode-detector-2.2.2.tgz",
+      "integrity": "sha512-JcSekql+EV93evfzF9zBr+Y6aRfkR+QFvgyzbwQ0dbymZXoAI9+WgT7H1E429f+3RKNncHz2CW98VQtaaKpmfQ==",
+      "dependencies": {
+        "@types/dom-webcodecs": "^0.1.11",
+        "zxing-wasm": "1.1.3"
+      }
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/buffer-equal": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
+      "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/cacheable-request": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
+      "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
+      "dependencies": {
+        "clone-response": "^1.0.2",
+        "get-stream": "^5.1.0",
+        "http-cache-semantics": "^4.0.0",
+        "keyv": "^3.0.0",
+        "lowercase-keys": "^2.0.0",
+        "normalize-url": "^4.1.0",
+        "responselike": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cacheable-request/node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cacheable-request/node_modules/lowercase-keys": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cardboard-vr-display": {
+      "version": "1.0.19",
+      "resolved": "https://registry.npmjs.org/cardboard-vr-display/-/cardboard-vr-display-1.0.19.tgz",
+      "integrity": "sha512-+MjcnWKAkb95p68elqZLDPzoiF/dGncQilLGvPBM5ZorABp/ao3lCs7nnRcYBckmuNkg1V/5rdGDKoUaCVsHzQ==",
+      "dependencies": {
+        "gl-preserve-state": "^1.0.0",
+        "nosleep.js": "^0.7.0",
+        "webvr-polyfill-dpdb": "^1.0.17"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "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"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/clone-response": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+      "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+      "dependencies": {
+        "mimic-response": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
+    "node_modules/custom-event-polyfill": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz",
+      "integrity": "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w=="
+    },
+    "node_modules/debug": {
+      "version": "2.2.0",
+      "resolved": "git+ssh://git@github.com/ngokevin/debug.git#ef5f8e66d49ce8bc64c6f282c15f8b7164409e3a",
+      "license": "MIT"
+    },
+    "node_modules/decompress-response": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
+      "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==",
+      "dependencies": {
+        "mimic-response": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/deep-assign": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-2.0.0.tgz",
+      "integrity": "sha512-2QhG3Kxulu4XIF3WL5C5x0sc/S17JLgm1SfvDfIRsR/5m7ZGmcejII7fZ2RyWhN0UWIJm0TNM/eKow6LAn3evQ==",
+      "dependencies": {
+        "is-obj": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/defer-to-connect": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
+      "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/dom-walk": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
+      "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
+    },
+    "node_modules/dtype": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz",
+      "integrity": "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg==",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/duplexer3": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
+      "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA=="
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/end-of-stream/node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
+      "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.19.12",
+        "@esbuild/android-arm": "0.19.12",
+        "@esbuild/android-arm64": "0.19.12",
+        "@esbuild/android-x64": "0.19.12",
+        "@esbuild/darwin-arm64": "0.19.12",
+        "@esbuild/darwin-x64": "0.19.12",
+        "@esbuild/freebsd-arm64": "0.19.12",
+        "@esbuild/freebsd-x64": "0.19.12",
+        "@esbuild/linux-arm": "0.19.12",
+        "@esbuild/linux-arm64": "0.19.12",
+        "@esbuild/linux-ia32": "0.19.12",
+        "@esbuild/linux-loong64": "0.19.12",
+        "@esbuild/linux-mips64el": "0.19.12",
+        "@esbuild/linux-ppc64": "0.19.12",
+        "@esbuild/linux-riscv64": "0.19.12",
+        "@esbuild/linux-s390x": "0.19.12",
+        "@esbuild/linux-x64": "0.19.12",
+        "@esbuild/netbsd-x64": "0.19.12",
+        "@esbuild/openbsd-x64": "0.19.12",
+        "@esbuild/sunos-x64": "0.19.12",
+        "@esbuild/win32-arm64": "0.19.12",
+        "@esbuild/win32-ia32": "0.19.12",
+        "@esbuild/win32-x64": "0.19.12"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.5",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
+      "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/gl-preserve-state": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/gl-preserve-state/-/gl-preserve-state-1.0.0.tgz",
+      "integrity": "sha512-zQZ25l3haD4hvgJZ6C9+s0ebdkW9y+7U2qxvGu1uWOJh8a4RU+jURIKEQhf8elIlFpMH6CrAY2tH0mYrRjet3Q=="
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/global": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
+      "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
+      "dependencies": {
+        "min-document": "^2.19.0",
+        "process": "^0.11.10"
+      }
+    },
+    "node_modules/got": {
+      "version": "9.6.0",
+      "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
+      "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
+      "dependencies": {
+        "@sindresorhus/is": "^0.14.0",
+        "@szmarczak/http-timer": "^1.1.2",
+        "cacheable-request": "^6.0.0",
+        "decompress-response": "^3.3.0",
+        "duplexer3": "^0.1.4",
+        "get-stream": "^4.1.0",
+        "lowercase-keys": "^1.0.1",
+        "mimic-response": "^1.0.1",
+        "p-cancelable": "^1.0.0",
+        "to-readable-stream": "^1.0.0",
+        "url-parse-lax": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/http-cache-semantics": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+      "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/immutable": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
+      "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
+      "dev": true
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-function": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
+      "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+      "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
+      "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ=="
+    },
+    "node_modules/keyv": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
+      "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+      "dependencies": {
+        "json-buffer": "3.0.0"
+      }
+    },
+    "node_modules/layout-bmfont-text": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/layout-bmfont-text/-/layout-bmfont-text-1.3.4.tgz",
+      "integrity": "sha512-mceomHZ8W7pSKQhTdLvOe1Im4n37u8xa5Gr0J3KPCHRMO/9o7+goWIOzZcUUd+Xgzy3+22bvoIQ0OaN3LRtgaw==",
+      "dependencies": {
+        "as-number": "^1.0.0",
+        "word-wrapper": "^1.0.7",
+        "xtend": "^4.0.0"
+      }
+    },
+    "node_modules/load-bmfont": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz",
+      "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==",
+      "dependencies": {
+        "buffer-equal": "0.0.1",
+        "mime": "^1.3.4",
+        "parse-bmfont-ascii": "^1.0.3",
+        "parse-bmfont-binary": "^1.0.5",
+        "parse-bmfont-xml": "^1.1.4",
+        "phin": "^2.9.1",
+        "xhr": "^2.0.1",
+        "xtend": "^4.0.0"
+      }
+    },
+    "node_modules/lowercase-keys": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+      "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.5",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
+      "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/map-limit": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
+      "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==",
+      "dependencies": {
+        "once": "~1.3.0"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mimic-response": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/min-document": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+      "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
+      "dependencies": {
+        "dom-walk": "^0.1.0"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/new-array": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/new-array/-/new-array-1.0.0.tgz",
+      "integrity": "sha512-K5AyFYbuHZ4e/ti52y7k18q8UHsS78FlRd85w2Fmsd6AkuLipDihPflKC0p3PN5i8II7+uHxo+CtkLiJDfmS5A=="
+    },
+    "node_modules/nice-color-palettes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/nice-color-palettes/-/nice-color-palettes-3.0.0.tgz",
+      "integrity": "sha512-lL4AjabAAFi313tjrtmgm/bxCRzp4l3vCshojfV/ij3IPdtnRqv6Chcw+SqJUhbe7g3o3BecaqCJYUNLswGBhQ==",
+      "dependencies": {
+        "got": "^9.2.2",
+        "map-limit": "0.0.1",
+        "minimist": "^1.2.0",
+        "new-array": "^1.0.0"
+      },
+      "bin": {
+        "nice-color-palettes": "bin/index.js"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/normalize-url": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
+      "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/nosleep.js": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/nosleep.js/-/nosleep.js-0.7.0.tgz",
+      "integrity": "sha512-Z4B1HgvzR+en62ghwZf6BwAR6x4/pjezsiMcbF9KMLh7xoscpoYhaSXfY3lLkqC68AtW+/qLJ1lzvBIj0FGaTA=="
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+      "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/p-cancelable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
+      "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-bmfont-ascii": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
+      "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="
+    },
+    "node_modules/parse-bmfont-binary": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
+      "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="
+    },
+    "node_modules/parse-bmfont-xml": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz",
+      "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==",
+      "dependencies": {
+        "xml-parse-from-string": "^1.0.0",
+        "xml2js": "^0.5.0"
+      }
+    },
+    "node_modules/parse-headers": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz",
+      "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="
+    },
+    "node_modules/phin": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
+      "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==",
+      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
+      "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.5.0",
+        "vue-demi": ">=0.14.5"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.4.0",
+        "typescript": ">=4.4.4",
+        "vue": "^2.6.14 || ^3.3.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia/node_modules/vue-demi": {
+      "version": "0.14.6",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz",
+      "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.4.33",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
+      "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/prepend-http": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
+      "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/present": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/present/-/present-0.0.6.tgz",
+      "integrity": "sha512-8HGGcsH0xefDkhtWzXhigzieKtervWPQgyX8RtQD3cKr4wU307j8XANVSaZLxbR0+1EBonCJNOdUrQ7hbk3Kiw=="
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/promise-polyfill": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-3.1.0.tgz",
+      "integrity": "sha512-t20OwHJ4ZOUj5fV+qms67oczphAVkRC6Rrjcrne+V1FJkQMym7n69xJmYyXHulm9OUQ0Ie5KSzg0QhOYgaxy+w=="
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/quad-indices": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/quad-indices/-/quad-indices-2.0.1.tgz",
+      "integrity": "sha512-6jtmCsEbGAh5npThXrBaubbTjPcF0rMbn57XCJVI7LkW8PUT56V+uIrRCCWCn85PSgJC9v8Pm5tnJDwmOBewvA==",
+      "dependencies": {
+        "an-array": "^1.0.0",
+        "dtype": "^2.0.0",
+        "is-buffer": "^1.0.2"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/recorder-core": {
+      "version": "1.3.24040900",
+      "resolved": "https://registry.npmjs.org/recorder-core/-/recorder-core-1.3.24040900.tgz",
+      "integrity": "sha512-QVOXHqrQhQ5FMgXTKh4P7oc31Qex2Q5skgBT972+0wrtwHr76zMnrybo48/VyUhS75whRxMFJ5yQpv+wj333Bw=="
+    },
+    "node_modules/responselike": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
+      "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==",
+      "dependencies": {
+        "lowercase-keys": "^1.0.0"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz",
+      "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.5"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.9.6",
+        "@rollup/rollup-android-arm64": "4.9.6",
+        "@rollup/rollup-darwin-arm64": "4.9.6",
+        "@rollup/rollup-darwin-x64": "4.9.6",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.9.6",
+        "@rollup/rollup-linux-arm64-gnu": "4.9.6",
+        "@rollup/rollup-linux-arm64-musl": "4.9.6",
+        "@rollup/rollup-linux-riscv64-gnu": "4.9.6",
+        "@rollup/rollup-linux-x64-gnu": "4.9.6",
+        "@rollup/rollup-linux-x64-musl": "4.9.6",
+        "@rollup/rollup-win32-arm64-msvc": "4.9.6",
+        "@rollup/rollup-win32-ia32-msvc": "4.9.6",
+        "@rollup/rollup-win32-x64-msvc": "4.9.6",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/sass": {
+      "version": "1.70.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
+      "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sax": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
+      "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
+    },
+    "node_modules/sdp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz",
+      "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw=="
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/super-animejs": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/super-animejs/-/super-animejs-3.1.0.tgz",
+      "integrity": "sha512-6MFAFJDRuvwkovxQZPruuyHinTa4rgj4hNLOndjcYYhZLckoXtVRY9rJPuq8p6c/tgZJrFYEAYAfJ2/hhNtUCA=="
+    },
+    "node_modules/super-three": {
+      "version": "0.158.0",
+      "resolved": "https://registry.npmjs.org/super-three/-/super-three-0.158.0.tgz",
+      "integrity": "sha512-dSqCbRHoQZD5ayz3WaOrUD2zuDfJhGyLIbGvhypUuICvb10cHLpNkxrrWOwA16SUOEPlE5z/rxTupjs41nzzhw=="
+    },
+    "node_modules/swiper": {
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.0.7.tgz",
+      "integrity": "sha512-cDfglW1B6uSmB6eB6pNmzDTNLmZtu5bWWa1vak0RU7fOI9qHjMzl7gVBvYSl34b0RU2N11HxxETJqQ5LeqI1cA==",
+      "funding": [
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/swiperjs"
+        },
+        {
+          "type": "open_collective",
+          "url": "http://opencollective.com/swiper"
+        }
+      ],
+      "engines": {
+        "node": ">= 4.7.0"
+      }
+    },
+    "node_modules/three-bmfont-text": {
+      "version": "3.0.0",
+      "resolved": "git+ssh://git@github.com/dmarcos/three-bmfont-text.git#eed4878795be9b3e38cf6aec6b903f56acd1f695",
+      "integrity": "sha512-1tv41kf1bo31dFvQeWyiAcurNg926wslmUQztsjbpIwEIJ7WqAQUOownrbHcAQVYlIdtcP4M+tXYaHzbUEF2GA==",
+      "license": "MIT",
+      "dependencies": {
+        "array-shuffle": "^1.0.1",
+        "layout-bmfont-text": "^1.2.0",
+        "nice-color-palettes": "^3.0.0",
+        "quad-indices": "^2.0.1"
+      }
+    },
+    "node_modules/to-readable-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
+      "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/url-parse-lax": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
+      "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==",
+      "dependencies": {
+        "prepend-http": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/vite": {
+      "version": "5.0.12",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
+      "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.19.3",
+        "postcss": "^8.4.32",
+        "rollup": "^4.2.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz",
+      "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/compiler-sfc": "3.4.15",
+        "@vue/runtime-dom": "3.4.15",
+        "@vue/server-renderer": "3.4.15",
+        "@vue/shared": "3.4.15"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-i18n": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.11.0.tgz",
+      "integrity": "sha512-vU4gY6lu8Pdfs9BgKGiDAJmFDf88cceR47KcSB0VW4xJzUrXR/7qwqM7A8dQ2nedhoIDxoOm5Ro4pFd2KvJqbA==",
+      "dependencies": {
+        "@intlify/core-base": "9.11.0",
+        "@intlify/shared": "9.11.0",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/vue-qrcode-reader": {
+      "version": "5.5.6",
+      "resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-5.5.6.tgz",
+      "integrity": "sha512-aKEm0OHVO4aj2VZSZPua0nJytSHqqAnnVlH5a6OBXhq9644NuQ4UAHXhXKbkmzJXN+9LuKhX2+C8SHNV8RXCjg==",
+      "dependencies": {
+        "barcode-detector": "2.2.2",
+        "webrtc-adapter": "8.2.3"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/vue-router": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
+      "integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/vuetify": {
+      "version": "3.5.14",
+      "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.5.14.tgz",
+      "integrity": "sha512-bmfid7K4D+wPi9h7sK4PxjmIB2tBzNuqlW14cs30iQ7GAphEeo/HYwn6aEdNK/Na+imhti8CJDDqdGs6SEfyXQ==",
+      "engines": {
+        "node": "^12.20 || >=14.13"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/johnleider"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.7",
+        "vite-plugin-vuetify": ">=1.0.0",
+        "vue": "^3.3.0",
+        "vue-i18n": "^9.0.0",
+        "webpack-plugin-vuetify": ">=2.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        },
+        "vite-plugin-vuetify": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        },
+        "webpack-plugin-vuetify": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webrtc-adapter": {
+      "version": "8.2.3",
+      "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz",
+      "integrity": "sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==",
+      "dependencies": {
+        "sdp": "^3.2.0"
+      },
+      "engines": {
+        "node": ">=6.0.0",
+        "npm": ">=3.10.0"
+      }
+    },
+    "node_modules/webvr-polyfill": {
+      "version": "0.10.12",
+      "resolved": "https://registry.npmjs.org/webvr-polyfill/-/webvr-polyfill-0.10.12.tgz",
+      "integrity": "sha512-trDJEVUQnRIVAnmImjEQ0BlL1NfuWl8+eaEdu+bs4g59c7OtETi/5tFkgEFDRaWEYwHntXs/uFF3OXZuutNGGA==",
+      "dependencies": {
+        "cardboard-vr-display": "^1.0.19"
+      }
+    },
+    "node_modules/webvr-polyfill-dpdb": {
+      "version": "1.0.18",
+      "resolved": "https://registry.npmjs.org/webvr-polyfill-dpdb/-/webvr-polyfill-dpdb-1.0.18.tgz",
+      "integrity": "sha512-O0S1ZGEWyPvyZEkS2VbyV7mtir/NM9MNK3EuhbHPoJ8EHTky2pTXehjIl+IiDPr+Lldgx129QGt3NGly7rwRPw=="
+    },
+    "node_modules/word-wrapper": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/word-wrapper/-/word-wrapper-1.0.7.tgz",
+      "integrity": "sha512-VOPBFCm9b6FyYKQYfn9AVn2dQvdR/YOVFV6IBRA1TBMJWKffvhEX1af6FMGrttILs2Q9ikCRhLqkbY2weW6dOQ=="
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+    },
+    "node_modules/xhr": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz",
+      "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==",
+      "dependencies": {
+        "global": "~4.4.0",
+        "is-function": "^1.0.1",
+        "parse-headers": "^2.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "node_modules/xml-parse-from-string": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
+      "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="
+    },
+    "node_modules/xml2js": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+      "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
+      "dependencies": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/zxing-wasm": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/zxing-wasm/-/zxing-wasm-1.1.3.tgz",
+      "integrity": "sha512-MYm9k/5YVs4ZOTIFwlRjfFKD0crhefgbnt1+6TEpmKUDFp3E2uwqGSKwQOd2hOIsta/7Usq4hnpNRYTLoljnfA==",
+      "dependencies": {
+        "@types/emscripten": "^1.39.10"
+      }
+    }
+  },
+  "dependencies": {
+    "@babel/parser": {
+      "version": "7.23.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz",
+      "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA=="
+    },
+    "@esbuild/aix-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
+      "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
+      "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
+      "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
+      "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
+      "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
+      "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
+      "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
+      "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
+      "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
+      "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
+      "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-loong64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
+      "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-mips64el": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
+      "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
+      "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-riscv64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
+      "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-s390x": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
+      "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
+      "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/netbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/openbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/sunos-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
+      "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
+      "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
+      "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
+      "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
+      "dev": true,
+      "optional": true
+    },
+    "@intlify/core-base": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.11.0.tgz",
+      "integrity": "sha512-cveOqAstjLZIiyatcP/HrzrQ87cZI8ScPQna3yvoM8zjcjcIRK1MRvmxUNlPdg0rTNJMZw7rixPVM58O5aHVPA==",
+      "requires": {
+        "@intlify/message-compiler": "9.11.0",
+        "@intlify/shared": "9.11.0"
+      }
+    },
+    "@intlify/message-compiler": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.11.0.tgz",
+      "integrity": "sha512-x31Gl7cscnoI4UUY1yaIy8e7vVMVW1VVlTXZz4SIHKqoSEUkfmgqK8NAx1e7RcoHEbICR7uyCbud0ZL1s4OGXQ==",
+      "requires": {
+        "@intlify/shared": "9.11.0",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "@intlify/shared": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.11.0.tgz",
+      "integrity": "sha512-KHSNgi7sRjmSm7aD8QH8WFt9VfKaekJuJ473opbJlkGY3EDnDUU8ikIhG8PbasQbgNvbY3m3tWNGqk2omIdwMA=="
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.4.15",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+    },
+    "@mdi/font": {
+      "version": "7.4.47",
+      "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz",
+      "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==",
+      "dev": true
+    },
+    "@rollup/rollup-android-arm-eabi": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz",
+      "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-android-arm64": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz",
+      "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-darwin-arm64": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz",
+      "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-darwin-x64": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz",
+      "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz",
+      "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz",
+      "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm64-musl": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz",
+      "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz",
+      "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-x64-gnu": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz",
+      "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-x64-musl": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz",
+      "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz",
+      "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz",
+      "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-x64-msvc": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz",
+      "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@sindresorhus/is": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
+      "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
+    },
+    "@szmarczak/http-timer": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
+      "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+      "requires": {
+        "defer-to-connect": "^1.0.1"
+      }
+    },
+    "@types/dom-webcodecs": {
+      "version": "0.1.11",
+      "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.11.tgz",
+      "integrity": "sha512-yPEZ3z7EohrmOxbk/QTAa0yonMFkNkjnVXqbGb7D4rMr+F1dGQ8ZUFxXkyLLJuiICPejZ0AZE9Rrk9wUCczx4A=="
+    },
+    "@types/emscripten": {
+      "version": "1.39.13",
+      "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.13.tgz",
+      "integrity": "sha512-cFq+fO/isvhvmuP/+Sl4K4jtU6E23DoivtbO4r50e3odaxAiVdbfSYRDdJ4gCdxx+3aRjhphS5ZMwIH4hFy/Cw=="
+    },
+    "@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
+    "@ungap/custom-elements": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@ungap/custom-elements/-/custom-elements-1.3.0.tgz",
+      "integrity": "sha512-f4q/s76+8nOy+fhrNHyetuoPDR01lmlZB5czfCG+OOnBw/Wf+x48DcCDPmMQY7oL8xYFL8qfenMoiS8DUkKBUw=="
+    },
+    "@vitejs/plugin-vue": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+      "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
+      "dev": true,
+      "requires": {}
+    },
+    "@vue/compiler-core": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz",
+      "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==",
+      "requires": {
+        "@babel/parser": "^7.23.6",
+        "@vue/shared": "3.4.15",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz",
+      "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==",
+      "requires": {
+        "@vue/compiler-core": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz",
+      "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==",
+      "requires": {
+        "@babel/parser": "^7.23.6",
+        "@vue/compiler-core": "3.4.15",
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/compiler-ssr": "3.4.15",
+        "@vue/shared": "3.4.15",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.5",
+        "postcss": "^8.4.33",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz",
+      "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==",
+      "requires": {
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "@vue/devtools-api": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
+      "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
+    },
+    "@vue/reactivity": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz",
+      "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==",
+      "requires": {
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz",
+      "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==",
+      "requires": {
+        "@vue/reactivity": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz",
+      "integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==",
+      "requires": {
+        "@vue/runtime-core": "3.4.15",
+        "@vue/shared": "3.4.15",
+        "csstype": "^3.1.3"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz",
+      "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==",
+      "requires": {
+        "@vue/compiler-ssr": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
+      "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g=="
+    },
+    "aframe": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/aframe/-/aframe-1.5.0.tgz",
+      "integrity": "sha512-QvxxF2jbQbSDnPouPveSm93OxfFUu5V6UmQoLbbf207BR/o4bM3trpMDCoGiYE2oQCe0d/VG56OyFmYDwYDfBg==",
+      "requires": {
+        "@ungap/custom-elements": "^1.1.0",
+        "buffer": "^6.0.3",
+        "custom-event-polyfill": "^1.0.6",
+        "debug": "github:ngokevin/debug#noTimestamp",
+        "deep-assign": "^2.0.0",
+        "load-bmfont": "^1.2.3",
+        "object-assign": "^4.0.1",
+        "present": "0.0.6",
+        "promise-polyfill": "^3.1.0",
+        "super-animejs": "^3.1.0",
+        "super-three": "0.158.0",
+        "three-bmfont-text": "github:dmarcos/three-bmfont-text#eed4878795be9b3e38cf6aec6b903f56acd1f695",
+        "webvr-polyfill": "^0.10.12"
+      }
+    },
+    "an-array": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/an-array/-/an-array-1.0.0.tgz",
+      "integrity": "sha512-M175GYI7RmsYu24Ok383yZQa3eveDfNnmhTe3OQ3bm70bEovz2gWenH+ST/n32M8lrwLWk74hcPds5CDRPe2wg=="
+    },
+    "animate.css": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz",
+      "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
+    },
+    "anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "ar.js": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/ar.js/-/ar.js-2.2.2.tgz",
+      "integrity": "sha512-ThNE9oOGcosmMRKVkkKgHgHhDpLKXikLkFGCviAka5L3GAP3mpU1S5xJ9UWviJDncfzDfyezDCGe+bm321nyqQ=="
+    },
+    "array-shuffle": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/array-shuffle/-/array-shuffle-1.0.1.tgz",
+      "integrity": "sha512-PBqgo1Y2XWSksBzq3GFPEb798ZrW2snAcmr4drbVeF/6MT/5aBlkGJEvu5A/CzXHf4EjbHOj/ZowatjlIiVidA=="
+    },
+    "as-number": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/as-number/-/as-number-1.0.0.tgz",
+      "integrity": "sha512-HkI/zLo2AbSRO4fqVkmyf3hms0bJDs3iboHqTrNuwTiCRvdYXM7HFhfhB6Dk51anV2LM/IMB83mtK9mHw4FlAg=="
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "axios": {
+      "version": "1.6.7",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
+      "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
+      "requires": {
+        "follow-redirects": "^1.15.4",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "barcode-detector": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/barcode-detector/-/barcode-detector-2.2.2.tgz",
+      "integrity": "sha512-JcSekql+EV93evfzF9zBr+Y6aRfkR+QFvgyzbwQ0dbymZXoAI9+WgT7H1E429f+3RKNncHz2CW98VQtaaKpmfQ==",
+      "requires": {
+        "@types/dom-webcodecs": "^0.1.11",
+        "zxing-wasm": "1.1.3"
+      }
+    },
+    "base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+    },
+    "binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "requires": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "buffer-equal": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
+      "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA=="
+    },
+    "cacheable-request": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
+      "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
+      "requires": {
+        "clone-response": "^1.0.2",
+        "get-stream": "^5.1.0",
+        "http-cache-semantics": "^4.0.0",
+        "keyv": "^3.0.0",
+        "lowercase-keys": "^2.0.0",
+        "normalize-url": "^4.1.0",
+        "responselike": "^1.0.2"
+      },
+      "dependencies": {
+        "get-stream": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+          "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+          "requires": {
+            "pump": "^3.0.0"
+          }
+        },
+        "lowercase-keys": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+          "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
+        }
+      }
+    },
+    "cardboard-vr-display": {
+      "version": "1.0.19",
+      "resolved": "https://registry.npmjs.org/cardboard-vr-display/-/cardboard-vr-display-1.0.19.tgz",
+      "integrity": "sha512-+MjcnWKAkb95p68elqZLDPzoiF/dGncQilLGvPBM5ZorABp/ao3lCs7nnRcYBckmuNkg1V/5rdGDKoUaCVsHzQ==",
+      "requires": {
+        "gl-preserve-state": "^1.0.0",
+        "nosleep.js": "^0.7.0",
+        "webvr-polyfill-dpdb": "^1.0.17"
+      }
+    },
+    "chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.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"
+      }
+    },
+    "clone-response": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+      "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+      "requires": {
+        "mimic-response": "^1.0.0"
+      }
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
+    "custom-event-polyfill": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz",
+      "integrity": "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w=="
+    },
+    "debug": {
+      "version": "git+ssh://git@github.com/ngokevin/debug.git#ef5f8e66d49ce8bc64c6f282c15f8b7164409e3a",
+      "from": "debug@github:ngokevin/debug#noTimestamp"
+    },
+    "decompress-response": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
+      "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==",
+      "requires": {
+        "mimic-response": "^1.0.0"
+      }
+    },
+    "deep-assign": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-2.0.0.tgz",
+      "integrity": "sha512-2QhG3Kxulu4XIF3WL5C5x0sc/S17JLgm1SfvDfIRsR/5m7ZGmcejII7fZ2RyWhN0UWIJm0TNM/eKow6LAn3evQ==",
+      "requires": {
+        "is-obj": "^1.0.0"
+      }
+    },
+    "defer-to-connect": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
+      "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+    },
+    "dom-walk": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
+      "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
+    },
+    "dtype": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz",
+      "integrity": "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg=="
+    },
+    "duplexer3": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
+      "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA=="
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "requires": {
+        "once": "^1.4.0"
+      },
+      "dependencies": {
+        "once": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+          "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+          "requires": {
+            "wrappy": "1"
+          }
+        }
+      }
+    },
+    "entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+    },
+    "esbuild": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
+      "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
+      "dev": true,
+      "requires": {
+        "@esbuild/aix-ppc64": "0.19.12",
+        "@esbuild/android-arm": "0.19.12",
+        "@esbuild/android-arm64": "0.19.12",
+        "@esbuild/android-x64": "0.19.12",
+        "@esbuild/darwin-arm64": "0.19.12",
+        "@esbuild/darwin-x64": "0.19.12",
+        "@esbuild/freebsd-arm64": "0.19.12",
+        "@esbuild/freebsd-x64": "0.19.12",
+        "@esbuild/linux-arm": "0.19.12",
+        "@esbuild/linux-arm64": "0.19.12",
+        "@esbuild/linux-ia32": "0.19.12",
+        "@esbuild/linux-loong64": "0.19.12",
+        "@esbuild/linux-mips64el": "0.19.12",
+        "@esbuild/linux-ppc64": "0.19.12",
+        "@esbuild/linux-riscv64": "0.19.12",
+        "@esbuild/linux-s390x": "0.19.12",
+        "@esbuild/linux-x64": "0.19.12",
+        "@esbuild/netbsd-x64": "0.19.12",
+        "@esbuild/openbsd-x64": "0.19.12",
+        "@esbuild/sunos-x64": "0.19.12",
+        "@esbuild/win32-arm64": "0.19.12",
+        "@esbuild/win32-ia32": "0.19.12",
+        "@esbuild/win32-x64": "0.19.12"
+      }
+    },
+    "estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "follow-redirects": {
+      "version": "1.15.5",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
+      "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw=="
+    },
+    "form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "optional": true
+    },
+    "get-stream": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+      "requires": {
+        "pump": "^3.0.0"
+      }
+    },
+    "gl-preserve-state": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/gl-preserve-state/-/gl-preserve-state-1.0.0.tgz",
+      "integrity": "sha512-zQZ25l3haD4hvgJZ6C9+s0ebdkW9y+7U2qxvGu1uWOJh8a4RU+jURIKEQhf8elIlFpMH6CrAY2tH0mYrRjet3Q=="
+    },
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "global": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
+      "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
+      "requires": {
+        "min-document": "^2.19.0",
+        "process": "^0.11.10"
+      }
+    },
+    "got": {
+      "version": "9.6.0",
+      "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
+      "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
+      "requires": {
+        "@sindresorhus/is": "^0.14.0",
+        "@szmarczak/http-timer": "^1.1.2",
+        "cacheable-request": "^6.0.0",
+        "decompress-response": "^3.3.0",
+        "duplexer3": "^0.1.4",
+        "get-stream": "^4.1.0",
+        "lowercase-keys": "^1.0.1",
+        "mimic-response": "^1.0.1",
+        "p-cancelable": "^1.0.0",
+        "to-readable-stream": "^1.0.0",
+        "url-parse-lax": "^3.0.0"
+      }
+    },
+    "http-cache-semantics": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+      "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+    },
+    "ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+    },
+    "immutable": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
+      "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true
+    },
+    "is-function": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
+      "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+      "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="
+    },
+    "json-buffer": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
+      "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ=="
+    },
+    "keyv": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
+      "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+      "requires": {
+        "json-buffer": "3.0.0"
+      }
+    },
+    "layout-bmfont-text": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/layout-bmfont-text/-/layout-bmfont-text-1.3.4.tgz",
+      "integrity": "sha512-mceomHZ8W7pSKQhTdLvOe1Im4n37u8xa5Gr0J3KPCHRMO/9o7+goWIOzZcUUd+Xgzy3+22bvoIQ0OaN3LRtgaw==",
+      "requires": {
+        "as-number": "^1.0.0",
+        "word-wrapper": "^1.0.7",
+        "xtend": "^4.0.0"
+      }
+    },
+    "load-bmfont": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz",
+      "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==",
+      "requires": {
+        "buffer-equal": "0.0.1",
+        "mime": "^1.3.4",
+        "parse-bmfont-ascii": "^1.0.3",
+        "parse-bmfont-binary": "^1.0.5",
+        "parse-bmfont-xml": "^1.1.4",
+        "phin": "^2.9.1",
+        "xhr": "^2.0.1",
+        "xtend": "^4.0.0"
+      }
+    },
+    "lowercase-keys": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+      "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
+    },
+    "magic-string": {
+      "version": "0.30.5",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
+      "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+      "requires": {
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      }
+    },
+    "map-limit": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
+      "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==",
+      "requires": {
+        "once": "~1.3.0"
+      }
+    },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+    },
+    "mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "requires": {
+        "mime-db": "1.52.0"
+      }
+    },
+    "mimic-response": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
+    },
+    "min-document": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+      "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
+      "requires": {
+        "dom-walk": "^0.1.0"
+      }
+    },
+    "minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
+    },
+    "nanoid": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
+    },
+    "new-array": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/new-array/-/new-array-1.0.0.tgz",
+      "integrity": "sha512-K5AyFYbuHZ4e/ti52y7k18q8UHsS78FlRd85w2Fmsd6AkuLipDihPflKC0p3PN5i8II7+uHxo+CtkLiJDfmS5A=="
+    },
+    "nice-color-palettes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/nice-color-palettes/-/nice-color-palettes-3.0.0.tgz",
+      "integrity": "sha512-lL4AjabAAFi313tjrtmgm/bxCRzp4l3vCshojfV/ij3IPdtnRqv6Chcw+SqJUhbe7g3o3BecaqCJYUNLswGBhQ==",
+      "requires": {
+        "got": "^9.2.2",
+        "map-limit": "0.0.1",
+        "minimist": "^1.2.0",
+        "new-array": "^1.0.0"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "normalize-url": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
+      "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="
+    },
+    "nosleep.js": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/nosleep.js/-/nosleep.js-0.7.0.tgz",
+      "integrity": "sha512-Z4B1HgvzR+en62ghwZf6BwAR6x4/pjezsiMcbF9KMLh7xoscpoYhaSXfY3lLkqC68AtW+/qLJ1lzvBIj0FGaTA=="
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
+    },
+    "once": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+      "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "p-cancelable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
+      "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw=="
+    },
+    "parse-bmfont-ascii": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
+      "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="
+    },
+    "parse-bmfont-binary": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
+      "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="
+    },
+    "parse-bmfont-xml": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz",
+      "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==",
+      "requires": {
+        "xml-parse-from-string": "^1.0.0",
+        "xml2js": "^0.5.0"
+      }
+    },
+    "parse-headers": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz",
+      "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="
+    },
+    "phin": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
+      "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="
+    },
+    "picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+    },
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true
+    },
+    "pinia": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
+      "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
+      "requires": {
+        "@vue/devtools-api": "^6.5.0",
+        "vue-demi": ">=0.14.5"
+      },
+      "dependencies": {
+        "vue-demi": {
+          "version": "0.14.6",
+          "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz",
+          "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
+          "requires": {}
+        }
+      }
+    },
+    "postcss": {
+      "version": "8.4.33",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
+      "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
+      "requires": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "prepend-http": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
+      "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="
+    },
+    "present": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/present/-/present-0.0.6.tgz",
+      "integrity": "sha512-8HGGcsH0xefDkhtWzXhigzieKtervWPQgyX8RtQD3cKr4wU307j8XANVSaZLxbR0+1EBonCJNOdUrQ7hbk3Kiw=="
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
+    },
+    "promise-polyfill": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-3.1.0.tgz",
+      "integrity": "sha512-t20OwHJ4ZOUj5fV+qms67oczphAVkRC6Rrjcrne+V1FJkQMym7n69xJmYyXHulm9OUQ0Ie5KSzg0QhOYgaxy+w=="
+    },
+    "proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "quad-indices": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/quad-indices/-/quad-indices-2.0.1.tgz",
+      "integrity": "sha512-6jtmCsEbGAh5npThXrBaubbTjPcF0rMbn57XCJVI7LkW8PUT56V+uIrRCCWCn85PSgJC9v8Pm5tnJDwmOBewvA==",
+      "requires": {
+        "an-array": "^1.0.0",
+        "dtype": "^2.0.0",
+        "is-buffer": "^1.0.2"
+      }
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "recorder-core": {
+      "version": "1.3.24040900",
+      "resolved": "https://registry.npmjs.org/recorder-core/-/recorder-core-1.3.24040900.tgz",
+      "integrity": "sha512-QVOXHqrQhQ5FMgXTKh4P7oc31Qex2Q5skgBT972+0wrtwHr76zMnrybo48/VyUhS75whRxMFJ5yQpv+wj333Bw=="
+    },
+    "responselike": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
+      "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==",
+      "requires": {
+        "lowercase-keys": "^1.0.0"
+      }
+    },
+    "rollup": {
+      "version": "4.9.6",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz",
+      "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==",
+      "dev": true,
+      "requires": {
+        "@rollup/rollup-android-arm-eabi": "4.9.6",
+        "@rollup/rollup-android-arm64": "4.9.6",
+        "@rollup/rollup-darwin-arm64": "4.9.6",
+        "@rollup/rollup-darwin-x64": "4.9.6",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.9.6",
+        "@rollup/rollup-linux-arm64-gnu": "4.9.6",
+        "@rollup/rollup-linux-arm64-musl": "4.9.6",
+        "@rollup/rollup-linux-riscv64-gnu": "4.9.6",
+        "@rollup/rollup-linux-x64-gnu": "4.9.6",
+        "@rollup/rollup-linux-x64-musl": "4.9.6",
+        "@rollup/rollup-win32-arm64-msvc": "4.9.6",
+        "@rollup/rollup-win32-ia32-msvc": "4.9.6",
+        "@rollup/rollup-win32-x64-msvc": "4.9.6",
+        "@types/estree": "1.0.5",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "sass": {
+      "version": "1.70.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
+      "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
+      "dev": true,
+      "requires": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      }
+    },
+    "sax": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
+      "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
+    },
+    "sdp": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz",
+      "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw=="
+    },
+    "source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+    },
+    "super-animejs": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/super-animejs/-/super-animejs-3.1.0.tgz",
+      "integrity": "sha512-6MFAFJDRuvwkovxQZPruuyHinTa4rgj4hNLOndjcYYhZLckoXtVRY9rJPuq8p6c/tgZJrFYEAYAfJ2/hhNtUCA=="
+    },
+    "super-three": {
+      "version": "0.158.0",
+      "resolved": "https://registry.npmjs.org/super-three/-/super-three-0.158.0.tgz",
+      "integrity": "sha512-dSqCbRHoQZD5ayz3WaOrUD2zuDfJhGyLIbGvhypUuICvb10cHLpNkxrrWOwA16SUOEPlE5z/rxTupjs41nzzhw=="
+    },
+    "swiper": {
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.0.7.tgz",
+      "integrity": "sha512-cDfglW1B6uSmB6eB6pNmzDTNLmZtu5bWWa1vak0RU7fOI9qHjMzl7gVBvYSl34b0RU2N11HxxETJqQ5LeqI1cA=="
+    },
+    "three-bmfont-text": {
+      "version": "git+ssh://git@github.com/dmarcos/three-bmfont-text.git#eed4878795be9b3e38cf6aec6b903f56acd1f695",
+      "integrity": "sha512-1tv41kf1bo31dFvQeWyiAcurNg926wslmUQztsjbpIwEIJ7WqAQUOownrbHcAQVYlIdtcP4M+tXYaHzbUEF2GA==",
+      "from": "three-bmfont-text@github:dmarcos/three-bmfont-text#eed4878795be9b3e38cf6aec6b903f56acd1f695",
+      "requires": {
+        "array-shuffle": "^1.0.1",
+        "layout-bmfont-text": "^1.2.0",
+        "nice-color-palettes": "^3.0.0",
+        "quad-indices": "^2.0.1"
+      }
+    },
+    "to-readable-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
+      "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q=="
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "url-parse-lax": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
+      "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==",
+      "requires": {
+        "prepend-http": "^2.0.0"
+      }
+    },
+    "vite": {
+      "version": "5.0.12",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
+      "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
+      "dev": true,
+      "requires": {
+        "esbuild": "^0.19.3",
+        "fsevents": "~2.3.3",
+        "postcss": "^8.4.32",
+        "rollup": "^4.2.0"
+      }
+    },
+    "vue": {
+      "version": "3.4.15",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz",
+      "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==",
+      "requires": {
+        "@vue/compiler-dom": "3.4.15",
+        "@vue/compiler-sfc": "3.4.15",
+        "@vue/runtime-dom": "3.4.15",
+        "@vue/server-renderer": "3.4.15",
+        "@vue/shared": "3.4.15"
+      }
+    },
+    "vue-i18n": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.11.0.tgz",
+      "integrity": "sha512-vU4gY6lu8Pdfs9BgKGiDAJmFDf88cceR47KcSB0VW4xJzUrXR/7qwqM7A8dQ2nedhoIDxoOm5Ro4pFd2KvJqbA==",
+      "requires": {
+        "@intlify/core-base": "9.11.0",
+        "@intlify/shared": "9.11.0",
+        "@vue/devtools-api": "^6.5.0"
+      }
+    },
+    "vue-qrcode-reader": {
+      "version": "5.5.6",
+      "resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-5.5.6.tgz",
+      "integrity": "sha512-aKEm0OHVO4aj2VZSZPua0nJytSHqqAnnVlH5a6OBXhq9644NuQ4UAHXhXKbkmzJXN+9LuKhX2+C8SHNV8RXCjg==",
+      "requires": {
+        "barcode-detector": "2.2.2",
+        "webrtc-adapter": "8.2.3"
+      }
+    },
+    "vue-router": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
+      "integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==",
+      "requires": {
+        "@vue/devtools-api": "^6.5.0"
+      }
+    },
+    "vuetify": {
+      "version": "3.5.14",
+      "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.5.14.tgz",
+      "integrity": "sha512-bmfid7K4D+wPi9h7sK4PxjmIB2tBzNuqlW14cs30iQ7GAphEeo/HYwn6aEdNK/Na+imhti8CJDDqdGs6SEfyXQ==",
+      "requires": {}
+    },
+    "webrtc-adapter": {
+      "version": "8.2.3",
+      "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz",
+      "integrity": "sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==",
+      "requires": {
+        "sdp": "^3.2.0"
+      }
+    },
+    "webvr-polyfill": {
+      "version": "0.10.12",
+      "resolved": "https://registry.npmjs.org/webvr-polyfill/-/webvr-polyfill-0.10.12.tgz",
+      "integrity": "sha512-trDJEVUQnRIVAnmImjEQ0BlL1NfuWl8+eaEdu+bs4g59c7OtETi/5tFkgEFDRaWEYwHntXs/uFF3OXZuutNGGA==",
+      "requires": {
+        "cardboard-vr-display": "^1.0.19"
+      }
+    },
+    "webvr-polyfill-dpdb": {
+      "version": "1.0.18",
+      "resolved": "https://registry.npmjs.org/webvr-polyfill-dpdb/-/webvr-polyfill-dpdb-1.0.18.tgz",
+      "integrity": "sha512-O0S1ZGEWyPvyZEkS2VbyV7mtir/NM9MNK3EuhbHPoJ8EHTky2pTXehjIl+IiDPr+Lldgx129QGt3NGly7rwRPw=="
+    },
+    "word-wrapper": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/word-wrapper/-/word-wrapper-1.0.7.tgz",
+      "integrity": "sha512-VOPBFCm9b6FyYKQYfn9AVn2dQvdR/YOVFV6IBRA1TBMJWKffvhEX1af6FMGrttILs2Q9ikCRhLqkbY2weW6dOQ=="
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+    },
+    "xhr": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz",
+      "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==",
+      "requires": {
+        "global": "~4.4.0",
+        "is-function": "^1.0.1",
+        "parse-headers": "^2.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "xml-parse-from-string": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
+      "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="
+    },
+    "xml2js": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+      "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
+      "requires": {
+        "sax": ">=0.6.0",
+        "xmlbuilder": "~11.0.0"
+      }
+    },
+    "xmlbuilder": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+    },
+    "zxing-wasm": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/zxing-wasm/-/zxing-wasm-1.1.3.tgz",
+      "integrity": "sha512-MYm9k/5YVs4ZOTIFwlRjfFKD0crhefgbnt1+6TEpmKUDFp3E2uwqGSKwQOd2hOIsta/7Usq4hnpNRYTLoljnfA==",
+      "requires": {
+        "@types/emscripten": "^1.39.10"
+      }
+    }
+  }
+}

+ 31 - 0
package.json

@@ -0,0 +1,31 @@
+{
+  "name": "ai-chatbot",
+  "version": "0.0.0",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build --base=./",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "aframe": "^1.5.0",
+    "animate.css": "^4.1.1",
+    "ar.js": "^2.2.2",
+    "axios": "^1.6.7",
+    "pinia": "^2.1.7",
+    "recorder-core": "^1.3.24040900",
+    "swiper": "^11.0.7",
+    "vue": "^3.3.11",
+    "vue-i18n": "^9.11.0",
+    "vue-qrcode-reader": "^5.5.6",
+    "vue-router": "^4.2.5",
+    "vuetify": "^3.5.14"
+  },
+  "devDependencies": {
+    "@mdi/font": "^7.4.47",
+    "@vitejs/plugin-vue": "^4.5.2",
+    "sass": "^1.70.0",
+    "vite": "^5.0.10"
+  }
+}

BIN
public/favicon.png


+ 28 - 0
src/App.vue

@@ -0,0 +1,28 @@
+<script setup>
+import { RouterLink, RouterView } from "vue-router";
+</script>
+
+<template>
+  <RouterView />
+</template>
+
+<style>
+.swiper-button-prev,
+.swiper-button-next {
+  color: var(--main-color) !important;
+}
+
+.swiper-button-prev:after,
+.swiper-button-next:after {
+  font-size: 35px;
+}
+
+.swiper-button-next,
+.swiper-rtl .swiper-button-prev {
+  right: var(--swiper-navigation-sides-offset, 0px) !important;
+}
+
+html.a-fullscreen .a-canvas {
+  border-radius: 10px !important;
+}
+</style>

+ 23 - 0
src/assets/base.css

@@ -0,0 +1,23 @@
+/* color palette from <https://github.com/vuejs/theme> */
+:root {
+  --main-color: #b78c5f;
+  --sub-color: #d0ba93;
+  --bg-grey: #9a9fa3;
+}
+
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+  margin: 0;
+  font-weight: normal;
+  font-family: "Noto Sans TC", sans-serif;
+}
+
+p {
+  white-space: pre-wrap; /* CSS3 */
+  white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+  white-space: -pre-wrap; /* Opera 4-6 */
+  white-space: -o-pre-wrap; /* Opera 7 */
+  word-wrap: break-word; /* Internet Explorer 5.5+ */
+}

+ 118 - 0
src/assets/img/angles-up-solid.svg

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="674px" height="589px" viewBox="0 0 674 589" enable-background="new 0 0 674 589" xml:space="preserve">  <image id="image0" width="674" height="589" x="0" y="0"
+    href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqIAAAJNCAQAAABAGt8vAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZ
+cwAAFxEAABcRAcom8z8AAAAHdElNRQfoAR0JNhuleParAAAYGElEQVR42u3d33UcyXnG4Vc+vhcz
+WGQgOAJDEZgZiI7A3AhMRWA6AnEjEBWBuRGIm8EyAzIC+QJLESTxZ2a6u+qrqufZqz0EurtmTv/w
+9cyg8bt/BIBL/UvvAwAYmYgCbCCiABuIKMAGIgqwgYgCbCCiABuIKMAGIgqwgYgCbCCiABuIKMAG
+IgqwgYgCbCCiABuIKMAGIgqwwb/2PgD4znVucpNn+fff/v9T3ufXvMu7/Nr70OBbv/PnQSjkKi/z
+PD88+O+/5HXe5mPvw4QvRJQqrvIqfzrh6z7ldV71Plj4TESp4WVe5fcnf/WHPM/73ocMiTeWqOBZ
+3uR/zkho8kP+nhe9DxsSkyj9Pcu7/OGi7/xJSOnPJEpflyc0+VPe9D58EFF62pLQREYpQETpZ2tC
+ExmlOxGllz0SmsgonYkofeyV0ERG6UpE6WHPhCYySkciSnt7JzSRUboRUVo7IqGJjNKJiNLWUQlN
+ZJQuRJSWjkxoIqN0IKK0c3RCExmlORGllRYJTWSUxkSUNlolNJFRmhJRWmiZ0ERGaUhEOV7rhCYy
+SjMiytF6JDSRURoRUY7VK6GJjNKEiHKknglNZJQGRJTj9E5oIqMcTkQ5SoWEJjLKwUSUY1RJaCKj
+HEpEOUKlhCYyyoFElP1VS2gioxxGRNlbxYQmMspBRJR9VU1oIqMcQkTZU+WEJjLKAUSU/VRPaCKj
+7E5E2csICU1klJ2JKPsYJaGJjLIrEWUPIyU0kVF2JKJsN1pCExllNyLKViMmNJFRdiKibDNqQhMZ
+ZRciyhYjJzSRUXYgolxu9IQmMspmIsqlZkhoIqNsJKJcZpaEJjLKJiLKJWZKaCKjbCCinG+2hCYy
+ysVElHPNmNBERrmQiHKeWROayCgXEVHOMXNCExnlAiLK6Xok9FPj/ckoZxJRTtUjoT/lKr803qeM
+chYR5TR9EvoiH3Mjo1QmopyiV0KTyCi1iShP65nQREYpTUR5Su+EJjJKYSLK4yokNJFRyhJRHlMl
+oYmMUpSI8rBKCU1klJJElIdUS2gioxQkotyvYkITGaUcEeU+VROayCjFiCjfq5zQREYpRUT5VvWE
+JjJKISLK10ZIaCKjlCGi3DVKQhMZpQgR5YuREprIKCWIKJ+NltBERilARLk1YkITGaU7ESUZN6GJ
+jNKZiDJ2QhMZpSsRZfSEJjJKRyK6uhkSmsgo3Yjo2mZJaCKjdCKiK5spoYmM0oWIrmu2hCYySgci
+uqoZE5rIKM2J6JpmTWgiozQmoiuaOaGJjNKUiK5n9oQmMkpDIrqaFRKayCjNiOhaVkloIqM0IqIr
+WSmhiYzShIiuY7WEJjJKAyK6ihUTmsgohxPRNaya0ERGOZiIrmDlhCYyyqFEdH6rJzSRUQ4korOT
+0FsyykFEdG4S+oWMcggRnZmEfk1GOYCIzktCvyej7E5EZyWh95NRdiaic5LQh8kouxLRGUno42SU
+HYnofCT0aTLKbkR0NhJ6GhllJyI6Fwk9nYyyCxGdiYSeR0bZgYjOQ0LPJ6NsJqKzkNDLyCgbiegc
+JPRyMsomIjoDCd1GRtlARMcnodvJKBcT0dFJ6D5klAuJ6NgkdD8yykVEdGQSui8Z5QIiOi4J3Z+M
+cjYRHZWEHkNGOZOIjklCjyOjnEVERyShx5JRziCi45HQ48koJxPR0UhoGzLKiUR0LBLajoxyEhEd
+iYS2JaOcQETHIaHtyShPEtFRSGgfMsoTRHQMEtqPjPIoER2BhPYlozxCROuT0P5klAeJaHUSWoOM
+8gARrU1C65BR7iWilUloLTLKPUS0LgmtR0b5johWJaE1ySjfENGaJLQuGeUrIlqRhNYmo9whovVI
+aH0yyj+JaDUSOgYZ5TciWouEjkNGSSKitUjoWGSUiGglEjoeGUVEy5DQMcno8kS0Bgkdl4wuTkQr
+kNCxyejSRLQ/CR2fjC5MRHuT0DnI6LJEtC8JnYeMLkpEe5LQucjokkS0Hwmdj4wuSER7kdA5yehy
+RLQPCZ2XjC5GRHuQ0LnJ6FJEtD0JnZ+MLkREW5PQNcjoMkS0LQldh4wuQkRbktC1yOgSRLQdCV2P
+jC5ARFuR0DXJ6PREtA0JXZeMTk5EW5DQtcno1ET0eBKKjE5MRI8moSQyOjERPZaE8pmMTkpEjySh
+3CWjUxLR40go35LRCYnoUSSU+8jodET0GBLKQ2R0MiJ6BAnlMTI6FRHdn4TyFBmdiIjuTUI5hYxO
+Q0T3JaGcSkYnIaJ7klDOIaNTENH9SCjnktEJiOheJJRLyOjwRHQfEsqlZHRwIroHCWULGR2aiG4n
+oWwlowMT0a0klD3I6LBEdBsJZS8yOigR3UJC2ZOMDklELyeh7E1GBySil5JQjiCjwxHRy0goR5HR
+wYjoJSSUI8noUET0fBLK0WR0ICJ6LgmlBRkdhoieR0JpRUYHIaLnkFBaktEhiOjpJJTWZHQAInoq
+CaUHGS1PRE8jofQio8WJ6CkklJ5ktDQRfZqE0puMFiaiT5FQKpDRskT0cRJKFTJalIg+RkKpREZL
+EtGHSSjVyGhBIvoQCaUiGS1HRO8noVQlo8WI6H0klMpktBQR/Z6EUp2MFiKi35JQRiCjZYjo1ySU
+UchoESJ6l4QyEhktQUS/kFBGI6MFiOhnEsqIZLQ7Eb0loYxKRjsT0URCGZuMdiWiEsr4ZLQjEZVQ
+ZiCj3aweUQllFjLaydoRlVBmIqNdrBxRCWU2MtrBuhGVUGYko82tGlEJZVYy2tiaEZVQZiajTa0Y
+UQlldjLa0HoRlVBWIKPNrBZRCWUVMtrIWhGVUFYio02sFFEJZTUy2sA6EZVQViSjh1slohLKqmT0
+YGtEVEJZmYweaoWISiirk9EDzR9RCQUZPdDsEZVQuCWjB5k7ohIKX8joIWaOqITC12T0APNGVELh
+ezK6u1kjKqFwPxnd2ZwRlVB4mIzuasaISig8TkZ3NF9EJRSeJqO7mS2iEgqnkdGdzBVRCYXTyegu
+ZoqohMJ5ZHQH80RUQuF8MrrZLBGVULiMjG40R0QlFC4no5vMEFEJhW1kdIPxIyqhsJ2MXmz0iEoo
+7ENGLzR2RCUU9iOjFxk5ohIK+5LRC4wbUQmF/cno2UaNqITCMWT0TGNGVELhODJ6lhEjKqFwLBk9
+w3gRlVA4noyebLSISii0IaMnGiuiEgrtyOhJRoqohEJbMnqCcSIqodCejD5plIhKKPQho08YI6IS
+Cv3I6KNGiKiEQl8y+oj6EZVQ6E9GH1Q9ohIKNcjoA2pHVEKhDhm9V+WISijUIqP3+N0/eh/BQyR0
+bVe//Zck7/Mx7/Ox9yGRxJn5naoR9USt6lme53lu8vvv/uVD3uZd3vY+QJydX6sZUU/Smq7yKs/v
+yeddH/I6b0ylnTlD76gYUU/Qip7lZf77xK/9kJcm0s6cpf9UL6KenBVd582Zz/rf8sI82pUz9TfV
+IuqJWdHzvHniIv4+v+RGRrtytiap9hEnT8qKXuSvFyQ0+UPe5Vnvg1+aDzwlqRbRcy/ptpPQ3l7k
+Lxd/r4z21iejL3ov+2uVIvoq/9F4jxLa25aEJjLaX4+M/iXXvZd9V53XRK/z98Z7lNDetib0ltdG
+e2v/MtyHXNd5zutMom8a709Ce9snoabR/tpPoz/kZe9Ff1FlEt3rhDqVhPa27zNuGu2t9TT6KVdV
+nvEqk+jLpnuT0N72/qFpGu2t9TT6+zqzaI1JtO3roRLa2zHXHabR3tpOox9+uz1NdzUm0RcN9yWh
+vR310o1ptLe20+gPuem94Fs1InrTbE8S2tuRr37LaG9tM/q893Jv1Yhoq0sACe3t6DcQZbS3lhm9
+7r3YWxUietNoPxLaW4vPYMhob+0yet17qbcqRPRZk71IaG+tPsYmo721yugld1w4QIWIXjfYh4T2
+1vKTwDLaW6uMXvVeaFIjoseT0N5a/zKFjPbWJqNXvZeZrBFRCe2tdUITGe2vRUY/9l5kUiOi7w/d
+uoT21iOhiYz2d3xG3/deYlIjoh8P3LaE9tYroYmM9tfjRnnNVYjo+8O2LKG99UxoIqP9HZnRn3sv
+7laFiH7Mh0O2K6G99U5oIqP9HZfR972XdqtCRJN3B2xTQnurkNBERvs7KqPvei/sVo2Ivt19ixLa
+W5WEJjLa3xEZ/XRANy5SJaL7XtBLaG+VEprIaH/7Z/Rt7yV9ViOiyasdtyWhvVVLaCKj/e2d0Ve9
+F/RZlYi+2W0WldDeKiY0kdH+9szoT/m193I+q3Fn+yR5nr/usBUJ7a1qQm+5+31v+9z9vtBfWKoz
+iSZv89PmbUhob7UTahrtb59p9EWdhFaKaPJy44Mrob1VT2gio/1tz+j/1nlTKal0OZ9sG/UltLcR
+EnrLRX1vU53plSbRLT+jyj2wyxknoabR/qY602tF9PbBPf83Yn+s98AuZqSEJjLa38fc5G9nf9ef
+K57p1SJ6++D+mE8nf/0v+be87n3QixstoYmM9vcxz8860z/kj3U+G3pXvYgmyetcn/Re/af8mOsq
+tyFY1ogJTWS0gte5OvFM/3Ouq/yu/LdqvbH0tau8yIv88MC//i1v86b3ITJsQm95i6mCx8/0X/I6
+bys/S5Ujeus6N7n66o/Zvcv7vKv8oC5k7IQmMlrHVW5yPeKZXj+i1DV+QhMZZaOar4kygjkS6rVR
+NhJRLjNLQhMZZRMR5RIzJTSRUTYQUc43W0ITGeViIsq5ZkxoIqNcSEQ5z6wJTWSUi4go55g5oYmM
+cgER5XTtE/opf268RxnlTCLKqXok9Cav8p+N9yqjnEVEOU2fhL5P8kZGqUxEOUW/hCYySmkiytP6
+JjSRUQoTUZ7SP6GJjFKWiPK4GglNZJSiRJTH1EloIqOUJKI8rFZCExmlIBHlIfUSmsgo5Ygo96uZ
+0ERGKUZEuU/dhCYySikiyvdqJzSRUQoRUb5VP6GJjFKGiPK1MRKayChFiCh3jZPQREYpQUT5YqyE
+JjJKASLKZ+MlNJFRuhNRbo2Z0ERG6UxESUZOaCKjdCWijJ7QREbpSEQZP6GJjNKNiK5ujoQmMkon
+Irq2eRKayChdiOjK5kpoIqN0IKLrmi+hiYzSnIiuas6EJjJKYyK6pnkTmsgoTYnoiuZOaCKjNCSi
+65k/oYmM0oyIrmaNhCYySiMiupZ1EprIKE2I6ErWSmgiozQgoutYL6GJjHI4EV3FmglNZJSDiega
+1k1oIqMcSkRXsHZCExnlQCI6PwlNZJTDiOjsJPQzGeUQIjo3Cb1LRjmAiM5MQr8lo+xOROclofeR
+UXYmorOS0IfIKLsS0TlJ6GNklB2J6Iwk9Ckyym5EdD4SegoZZSciOhsJPZWMsgsRnYuEnkNG2YGI
+zkRCzyWjbCai85DQS8goG4noLCT0UjLKJiI6BwndQkbZQERnIKFbySgXE9HxSegeZJQLiejoJHQv
+MspFRHRsEronGeUCIjoyCd2bjHI2ER2XhB5BRjmTiI5KQo8io5xFRMckoUeSUc4goiOS0KPJKCcT
+0fFIaAsyyolEdDQS2oqMchIRHYuEtiSjnEBERyKhrckoTxLRcUhoDzLKE0R0FBLai4zyKBEdg4T2
+JKM8QkRHIKG9ySgPEtH6JLQCGeUBIlqdhFYho9xLRGuT0EpklHuIaGUSWo2M8h0RrUtCK5JRviGi
+VUloVTLKV0S0JgmtTEa5Q0QrktDqZJR/EtF6JHQEMspvRLQaCR2FjJJERKuR0JHIKBHRWiR0NDKK
+iBYioSOS0eWJaBUSOioZXZyI1iChI5PRpYloBRI6OhldmIj2J6EzkNFliWhvEjoLGV2UiPYloTOR
+0SWJaE8SOhsZXZCI9iOhM5LR5YhoLxI6KxldjIj2IaEzk9GliGgPEjo7GV2IiLYnoSuQ0WWIaGsS
+ugoZXYSItiWhK5HRJYhoSxK6GhldgIi2I6ErktHpiWgrEroqGZ2ciLYhoSuT0amJaAsSujoZnZiI
+Hk9CkdGJiejRJJRbMjopET2WhPKFjE5JRI8koXxNRickoseRUL4no9MR0aNIKPeT0cmI6DEklIfJ
+6FRE9AgSyuNkdCIiuj8J5WkyOg0R3ZuEchoZnYSI7ktCOZ2MTkFE9yShnEdGJyCi+5FQziejwxPR
+vUgol5HRwYnoPiSUy8no0ER0DxLKNjI6MBHdTkLZTkaHJaJbSSj7kNFBieg2Esp+ZHRIIrqFhLIv
+GR2QiF5OQtmfjA5HRC8loRxDRgcjopeRUI4jo0MR0UtIKMeS0YGI6PkklOPJ6DBE9FwSShsyOggR
+PY+E0o6MDkFEzyGhtCWjAxDR00ko7cloeSJ6KgmlDxktTkRPI6H0I6OliegpJJS+ZLQwEX2ahNKf
+jJYlok+RUGqQ0aJE9HESSh0yWpKIPkZCqUVGCxLRh0ko9choOSL6EAmlJhktRkTvJ6HUJaOliOh9
+JJTaZLQQEf2ehFKfjJYhot+SUMYgo0WI6NcklHHIaAkiepeEMhYZLUBEv5BQxiOj3YnoZxLKmGS0
+MxG9JaGMS0a7EtFEQhmdjHYkohLKDGS0GxGVUOYgo52sHlEJZR4y2sXaEZVQ5iKjHawcUQllPjLa
+3LoRlVDmJKONrRpRCWVeMtrUmhGVUOYmow2tGFEJZX4y2sx6EZVQ1iCjjawWUQllHTLaxFoRlVDW
+IqMNrBRRCWU9Mnq4dSIqoaxJRg+2SkQllHXJ6KHWiKiEsjYZPdAKEZVQkNHDzB9RCYVERg8ze0Ql
+FD6T0UPMHVEJhbtk9AAzR1RC4Vsyurt5IyqhcB8Z3dmsEZVQeIiM7mrOiEooPEZGdzRjRCUUniKj
+u5kvohIKp5DRncwWUQmFU8noLuaKqITCOWR0BzNFVELhXDK62TwRlVC4hIxuNEtEJRQuJaObzBFR
+CYUtZHSDGSIqobCVjF5s/IhKKOxBRi80ekQlFPYioxcZO6ISCnuS0QuMHFEJhb3J6NnGjaiEwhFk
+9EyjRlRC4SgyepYxIyqhcCQZPcOIEZVQOJqMnmy8iEootCCjJxotohIKrcjoScaKqIRCSzJ6gpEi
+KqHQmow+aZyISij0IKNPGCWiEgq9yOijxoiohEJPMvqIESIqodCbjD6ofkQlFCqQ0QdUj6iEQhUy
+eq/aEZVQqERG71E5ohIK1cjod+pGVEKhIhn9RtWISihUJaNfqRlRCYXKZPSOihGVUKhORv+pXkQl
+FEYgo7+pFlEJhVHIaJLkd//ofQR33eT/Gu9RQmGL9mPPL7nJx97LvqtSRK/zLr9vukcJha16ZPS6
+96LvqnQ5/0ZCYTg9Lupf9170XXUi+ip/aLo/CYV9tM/of+Wm96K/qHI5f5X3TedQCYU9tb6o/7lO
+RqtMoi8lFAbWehr99zoRrTGJPsuvDSMqoXCEttPo3/K894Jv1ZhEn0soDK/tNPofVT4xWiWirUgo
+HKdtRp/3Xu6tGhG9abQfCYVjtczoTe/F3qoQ0atGF/MSCsdrl9Hr3ku9VSOiLUgotNEqo20/Wf6g
+ChG9brAPCYV22n/8vqMKEX12+B4kFNpqk9Gb3stMakT0aBIK7S0zjc4fUQmFPo7P6PveS0xqRPTd
+gduWUOjn6Ix+7L3ApEZEPx62ZQmFvo7M6Ifei7tVIaLvD9quhEJ/x2X0fe+l3aoQ0eTnA7YpoVDD
+URl913tht2pE9O3uW5RQqOOYjL7tvaxbc0ZUQqGW/TP6S37tvahbNSL6a37acWsSCvXsndHXvRf0
+WY2bMu/5x5IlFKra77bNHxrdc+MENSbR5N1Os6iEQl37TaMvey/liyqT6D5/IkRCobo9ptFCf6au
+ziSafMyLjVuQUKhv+zT6qco97W/ViWjyNj9u+G4JhTFsy+in3NT4dc/P6lzO33qTP130fRIKI7n0
+or7gmV5pEk2SFxf9jCr4wAKPuGwaLXmmV4to8iZ/zKezvuPnXNV7YIFHvcm/nXkLkZ9zXfFMrxfR
+5F2uTv7A06f8Z7VXSICTvM91/vfEr/2UH3NT5XeUvlbtNdEvrvLqiddHP+RNXgsoDO3pM/1TXlc+
+0+tGNEme5Xme5+a7z49+yLu8rXL7AWCjoc/02hH97CpXSa7zMb8meV/3ZxKwwZBn+hgRBSiq4htL
+AMMQUYANRBRgAxEF2EBEATYQUYANRBRgAxEF2EBEATYQUYANRBRgAxEF2EBEATYQUYANRBRgAxEF
+2EBEATYQUYANRBRgAxEF2EBEATYQUYANRBRgAxEF2EBEATYQUYANRBRgAxEF2OD/AdmpbEXVqyph
+AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI0LTAxLTI5VDA5OjU0OjI3KzAwOjAw9jPUkAAAACV0RVh0
+ZGF0ZTptb2RpZnkAMjAyNC0wMS0yOVQwOTo1NDoyNyswMDowMIdubCwAAAAodEVYdGRhdGU6dGlt
+ZXN0YW1wADIwMjQtMDEtMjlUMDk6NTQ6MjcrMDA6MDDQe03zAAAAAElFTkSuQmCC" />
+</svg>

BIN
src/assets/img/banner-1.jpg


BIN
src/assets/img/banner-2.jpg


BIN
src/assets/img/banner.jpg


BIN
src/assets/img/icon/素材-01.png


BIN
src/assets/img/icon/素材-02.png


BIN
src/assets/img/icon/素材-03.png


BIN
src/assets/img/icon/素材-04.png


BIN
src/assets/img/icon/素材-05.png


BIN
src/assets/img/icon/素材-06.png


BIN
src/assets/img/icon/素材-07.png


BIN
src/assets/img/icon/素材-08.png


BIN
src/assets/img/icon/素材-09.png


BIN
src/assets/img/icon/素材-10.png


BIN
src/assets/img/icon/素材-15.png


+ 1 - 0
src/assets/img/location-dot-solid.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M215.7 499.2C267 435 384 279.4 384 192C384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2c12.3 15.3 35.1 15.3 47.4 0zM192 128a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/></svg>

+ 51 - 0
src/assets/img/logo.svg

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 280 120" style="enable-background:new 0 0 280 120;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#231F20;}
+	.st1{fill:#B1B3B5;}
+	.st2{fill:#C6A880;}
+	.st3{fill:#AE774F;}
+</style>
+<polygon class="st0" points="6.9,83.4 5.4,87.2 14.9,87.2 14.9,110.1 13.8,111.8 19.5,111.8 19.5,87.2 27.9,87.2 27.9,83.4 "/>
+<path class="st0" d="M79.8,86.1c-1.8-1.8-4.7-2.7-8.6-2.7h-8.6l1.1,1.7v25l-1.1,1.7H68V87.2h3.3c1.5,0,3.6,0.5,4.9,1.8
+	c0.8,0.8,1.2,1.8,1.2,2.9c0,1.4-0.4,2.5-1.2,3.3c-1.4,1.4-3.5,1.6-4.3,1.6h-3.3l2.7,3.8h0.5c2.8,0,5.2-0.7,7-1.9
+	c2.1-1.5,3.1-3.7,3.1-6.7C82,89.5,81.2,87.5,79.8,86.1z"/>
+<polygon class="st0" points="50.5,83.4 51.6,85.1 51.6,110.1 50.5,111.8 56.1,111.8 56.1,83.4 "/>
+<polygon class="st0" points="106.4,83.4 107.5,85.1 107.5,110.1 106.4,111.8 112.1,111.8 112.1,83.4 "/>
+<polygon class="st0" points="89.7,108.1 89.7,99 100.1,99 100.1,95.2 89.7,95.2 89.7,87.2 101.2,87.2 101.2,83.4 84.1,83.4 
+	85.2,85.1 85.2,110.1 84.1,111.8 102,111.8 103,108.1 "/>
+<g>
+	<polygon class="st0" points="36.4,82.9 36.4,82.9 36.4,82.9 	"/>
+	<path class="st0" d="M36.4,82.9c-0.1-0.2-0.2-0.3-0.4-0.3c-0.2,0-0.3,0.1-0.4,0.3l-12.3,28.9h6.5l-1-1.4l7.2-17.7l7.2,17.7l-1,1.4
+		h6.5L36.4,82.9z"/>
+</g>
+<polygon class="st1" points="134.3,110.1 134.3,83.4 128.6,83.4 129.7,85.1 129.7,110.1 128.6,111.8 135.4,111.8 "/>
+<polygon class="st1" points="175.6,110.1 175.6,83.4 169.9,83.4 171,85.1 171,110.1 169.9,111.8 176.7,111.8 "/>
+<g>
+	<g>
+		<path class="st1" d="M162.3,86.5c2.7,2.7,4.1,6.6,4.1,11.1c0,4.3-1.3,8-3.6,10.6c-2.4,2.7-5.9,4.2-10.2,4.2
+			c-3.9,0-7.3-1.2-9.7-3.7c-2.7-2.7-4-6.5-4-11.1c0-4.5,1.4-8.4,4.1-11.1c2.4-2.4,5.8-3.7,9.6-3.7C156.5,82.8,159.9,84.1,162.3,86.5
+			z M158.8,106.2c2-2,3-5,3-8.6c0-3.5-1.1-6.6-3.1-8.6c-1.6-1.6-3.7-2.3-6.1-2.3c-2.4,0-4.5,0.8-6.1,2.3c-2,2-3.1,5.1-3.1,8.6
+			c0,3.6,1.1,6.6,3,8.6c1.6,1.6,3.7,2.4,6.2,2.4C155.2,108.6,157.2,107.8,158.8,106.2z"/>
+	</g>
+	<g>
+		<path class="st1" d="M162.3,86.5c2.7,2.7,4.1,6.6,4.1,11.1c0,4.3-1.3,8-3.6,10.6c-2.4,2.7-5.9,4.2-10.2,4.2
+			c-3.9,0-7.3-1.2-9.7-3.7c-2.7-2.7-4-6.5-4-11.1c0-4.5,1.4-8.4,4.1-11.1c2.4-2.4,5.8-3.7,9.6-3.7C156.5,82.8,159.9,84.1,162.3,86.5
+			z M158.8,106.2c2-2,3-5,3-8.6c0-3.5-1.1-6.6-3.1-8.6c-1.6-1.6-3.7-2.3-6.1-2.3c-2.4,0-4.5,0.8-6.1,2.3c-2,2-3.1,5.1-3.1,8.6
+			c0,3.6,1.1,6.6,3,8.6c1.6,1.6,3.7,2.4,6.2,2.4C155.2,108.6,157.2,107.8,158.8,106.2z"/>
+	</g>
+</g>
+<g>
+	<path class="st2" d="M274.7,37.6c0,16.5-13.4,29.9-29.9,29.9c-16.5,0-29.9-13.4-29.9-29.9c0-16.5,13.4-29.9,29.9-29.9
+		C261.3,7.7,274.7,21.1,274.7,37.6z M250.2,22c0-1.3-1.1-2.4-2.4-2.4h-6c-1.3,0-2.4,1.1-2.4,2.4v31.1c0,1.3,1.1,2.4,2.4,2.4h6
+		c1.3,0,2.4-1.1,2.4-2.4V22z"/>
+	<path class="st2" d="M182.5,37.6c0,16.5-13.4,29.9-29.9,29.9c-16.5,0-29.9-13.4-29.9-29.9c0-16.5,13.4-29.9,29.9-29.9
+		C169.1,7.7,182.5,21.1,182.5,37.6z M158,22c0-1.3-1.1-2.4-2.4-2.4h-6c-1.3,0-2.4,1.1-2.4,2.4v31.1c0,1.3,1.1,2.4,2.4,2.4h6
+		c1.3,0,2.4-1.1,2.4-2.4V22z"/>
+	<path class="st3" d="M228.6,37.6c0,16.5-13.4,29.9-29.9,29.9c-16.5,0-29.9-13.4-29.9-29.9c0-16.5,13.4-29.9,29.9-29.9
+		C215.2,7.7,228.6,21.1,228.6,37.6z M208.3,30.4c0-1.3-1.1-2.4-2.4-2.4h-14.4c-1.3,0-2.4,1.1-2.4,2.4v14.4c0,1.3,1.1,2.4,2.4,2.4
+		h14.4c1.3,0,2.4-1.1,2.4-2.4V30.4z"/>
+</g>
+</svg>

File diff suppressed because it is too large
+ 0 - 0
src/assets/img/map/b1.svg


+ 1 - 0
src/assets/img/paper-plane-solid.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6L284 427.7l-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1S160 493.2 160 480V396.4c0-4 1.5-7.8 4.2-10.7L331.8 202.8c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7L106 360.8 17.7 316.6C7.1 311.3 .3 300.7 0 288.9s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z"/></svg>

BIN
src/assets/img/pause-button.png


+ 1 - 0
src/assets/img/phone-solid.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C12.1 30.2 0 46 0 64C0 311.4 200.6 512 448 512c18 0 33.8-12.1 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z"/></svg>

BIN
src/assets/img/play-button.png


+ 1 - 0
src/assets/logo.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

+ 1 - 0
src/assets/main.css

@@ -0,0 +1 @@
+@import './base.css';

BIN
src/assets/video/cupola_000090.mp4


BIN
src/assets/video/start.mp4


BIN
src/assets/video/start_1.mp4


BIN
src/assets/video/路線24-26-27-v3.mp4


+ 23 - 0
src/components/ArGuided.vue

@@ -0,0 +1,23 @@
+<script>
+import "aframe";
+// import 'ar.js';
+</script>
+
+<template>
+  <div>
+    <a-scene>
+      <a-assets>
+        <video
+          id="vr-video"
+          src="../assets/video/路線24-26-27-v3.mp4"
+          autoplay
+          ecrossorigin="anonymous"
+        ></video>
+      </a-assets>
+
+      <a-sky src="#vr-video"></a-sky>
+    </a-scene>
+  </div>
+</template>
+
+<style></style>

+ 4288 - 0
src/components/Chat.vue

@@ -0,0 +1,4288 @@
+<script setup>
+import { ref, reactive, onMounted, watch, nextTick } from "vue";
+import { useRoute } from "vue-router";
+// VR
+import "aframe";
+// i18n
+import { useI18n } from "vue-i18n";
+// Axios
+import axios from "axios";
+// Moment
+// import moment from "moment";
+// Animate
+import "animate.css";
+// Swiper
+import { Swiper, SwiperSlide } from "swiper/vue";
+import { Navigation, Autoplay } from "swiper/modules";
+import "swiper/css";
+import "swiper/css/navigation";
+// Recorder
+import Recorder from "recorder-core";
+import "recorder-core/src/engine/mp3";
+import "recorder-core/src/engine/mp3-engine";
+// QR Code
+import { QrcodeStream, QrcodeDropZone, QrcodeCapture } from "vue-qrcode-reader";
+// Components
+// import Navbar from "../components/Navbar.vue";
+// import TicketPurchase from "../components/TicketPurchase.vue";
+
+const { t, locale } = useI18n();
+
+const route = useRoute();
+const routeParam = ref(null);
+
+let qrCodeDialog = ref(false);
+let qrCodeLoading = ref(true);
+let location = ref(""); // 當前位置
+
+// QR Code 掃描
+function onDetect(detectedCodes) {
+  console.log("detectedCodes", detectedCodes);
+
+  const url = new URL(detectedCodes[0].rawValue);
+  location.value = url.searchParams.get("location");
+
+  let val;
+  let lang = getLang();
+
+  if (lang === "en") {
+    switch (location.value) {
+      case "B1 鼎泰豐":
+        val = "b1 Din Tai Fung";
+        break;
+      case "B1 中環":
+        val = "b1 Centre";
+        break;
+      case "B1 2號電梯":
+        val = "b1 Elevator No.2";
+        break;
+      case "B1 4號電梯":
+        val = "b1 Elevator No.4";
+        break;
+      case "B1 3號電梯":
+        val = "b1 Elevator No.3";
+        break;
+      case "1F 信義環":
+        val = "1f South Xinyi";
+        break;
+      case "1F 3號電梯":
+        val = "1f Elevator No.3";
+        break;
+      case "2F 3號電梯":
+        val = "2f Elevator No.3";
+        break;
+      case "88F 觀景台":
+        val = "88f Observatory";
+        break;
+      case "89F 觀景台":
+        val = "89f Observatory";
+        break;
+
+      default:
+        break;
+    }
+  } else if (lang === "ch") {
+    val = location.value;
+  }
+
+  if (location.value && location.value !== "") {
+    qrCodeDialog.value = false;
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: `${t("current_location")}:${val}`,
+    });
+
+    changeLocation(location.value); // 選擇導覽位置
+  } else {
+    qrCodeDialog.value = false;
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: "讀取錯誤,請重新掃描。",
+    });
+  }
+}
+
+// 判斷 vue-qrcode-reader 是否加載完成
+function onInit(capabilities) {
+  qrCodeLoading.value = false;
+}
+
+onMounted(() => {
+  // 取得當前路由參數
+  routeParam.value = route.path.replace("/", "");
+  console.log("網址參數", routeParam.value);
+  window.addEventListener("resize", handleResize);
+});
+
+const userMessage = ref("");
+let modules = [Navigation, Autoplay]; // Swiper
+
+// AI 客服回覆訊息
+let messages = ref([]);
+
+watch(messages.value, (val) => {
+  console.log("messages", val);
+  scrollToBottom();
+  updateMenuHeight();
+});
+
+let ad = ref({}); // 彈跳視窗廣告
+let qaQuery = reactive([]);
+
+const chatArea = ref(null); // 對話框
+
+// 滾動到對話框底部
+const scrollToBottom = () => {
+  setTimeout(() => {
+    // chatArea.value.scrollTop = chatArea.value.scrollHeight;
+    console.log("chatArea.value", chatArea.value.scrollHeight);
+
+    chatArea.value.scrollTo({
+      top: chatArea.value.scrollHeight,
+      behavior: "smooth",
+    });
+
+    // window.scrollTo({
+    //   top: document.body.scrollHeight,
+    //   behavior: "smooth", // 平滑滾動
+    // });
+  }, 100);
+
+  // let list = messages.value;
+  // const item = list[list.length - 1];
+  // if (item.label === "text") {
+  //   setTimeout(() => {
+  //     chatArea.value.scrollTop = chatArea.value.scrollHeight;
+  //   }, 100);
+  // }
+};
+
+// 對話選項(按鈕)
+function setBtnValue(val) {
+  console.log("value", val);
+  userMessage.value = val;
+  sendMessage("text");
+}
+
+// 傳送訊息 (如 type="text" 代表為純文字訊息,不需語音回覆)
+async function sendMessage(type = "") {
+  if (userMessage.value === "") {
+    return;
+  }
+
+  console.log("sendMessage", userMessage.value);
+  qaQuery.push(userMessage.value);
+  let message = userMessage.value;
+  userMessage.value = "";
+  let url = "https://devbox10.itri-nlp.tw:38125/v1/qa/";
+
+  try {
+    // 使用者訊息
+    messages.value.push({
+      label: "text",
+      body: message,
+      author: "user",
+    });
+
+    videoLoading.value = true;
+    let isVideoCache = await getVideoCache(message); // 判斷使用者問題是否有 Video Cache
+
+    console.log("isVideoCache", isVideoCache);
+
+    // 如果有 Video Cache 則不需回傳工研院回答
+    if (isVideoCache) {
+      handleVideoCache(); // 播放 Video Cache
+      return;
+    }
+
+    axios
+      .post(
+        url,
+        new URLSearchParams({
+          query: JSON.stringify(qaQuery), // 使用者問句
+          language: selectLang.value, // 語系
+          category: assignCategory.value, // 類別
+          subtype: assignSubtype.value, // 詳細類別(非必填)
+          from_loc: "", // 定位點
+          to_loc: "", // 導覽點
+        }),
+        {
+          headers: {
+            "Content-Type": "application/x-www-form-urlencoded",
+          },
+        }
+      )
+      .then(async (response) => {
+        // if (showAnchor.value) {
+        //   getVideo(response.data);
+        // }
+
+        // AI 客服回傳訊息
+        messages.value.push({
+          label: "text",
+          author: "ai",
+          body: response.data.response,
+        });
+
+        console.log("response", response);
+
+        if (type !== "text") {
+          handleTTS(response.data.response); // 取得語音回覆
+        }
+
+        if (response.data.data.length) {
+          let info = {
+            buttonList: [], // 按鈕
+            ticketList: [], // 票券
+          };
+          let labelContent;
+
+          response.data.data.map((item) => {
+            if (item.type === "button") {
+              console.log("按鈕");
+              info.buttonList.push(item);
+              labelContent = "ticket";
+            } else if (item.type === "ticket") {
+              console.log("票");
+              info.ticketList.push(item);
+              labelContent = "ticket";
+            } else if (item.type === "brand") {
+              console.log("品牌");
+              info.ticketList.push(item);
+              labelContent = "brand";
+            }
+          });
+
+          console.log("info", info);
+          console.log("labelContent", labelContent);
+
+          setTimeout(() => {
+            messages.value.push({
+              label: labelContent,
+              author: "ai",
+              body: info,
+            });
+          }, 10);
+        }
+      })
+      .catch((error) => {
+        console.error("Error:", error);
+      });
+
+    console.log("messages.value", messages.value);
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+let hideMenu = ref(true); // 底部選單
+let showInput = ref(true); // 輸入框
+
+function getLang() {
+  let lang = localStorage.getItem("lang");
+  let langVal = "";
+
+  switch (lang) {
+    case "zh-tw":
+      langVal = "ch";
+      break;
+    case "en-us":
+      langVal = "en";
+      break;
+
+    default:
+      break;
+  }
+
+  return langVal;
+}
+
+let showAd = ref(false);
+
+// 取得廣告
+async function setAd() {
+  let lang = getLang();
+  console.log("lang", lang);
+  let url = `https://cmm.ai:9101/ad?language=${lang}`;
+
+  try {
+    const response = await axios.get(url);
+    console.log("Ad response", response);
+
+    ad.value = response.data.data.body;
+    console.log("Ad", ad.value);
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+let video = ref(null);
+
+function videoPlay() {
+  video.value.load();
+  video.value.play();
+}
+
+// 底部選單
+const menu = ref(null);
+const menuHeight = ref(0);
+
+let isRotate = ref(false);
+let isLanguagePage = ref(true);
+let selectLang = ref("");
+let chatLoading = ref(true);
+
+watch(isLanguagePage, (val) => {
+  if (!val) {
+    setTimeout(updateMenuHeight, 500);
+  }
+});
+
+const handleResize = () => {
+  updateMenuHeight();
+};
+
+// 取得底部選單高度
+const updateMenuHeight = () => {
+  nextTick(() => {
+    if (menu.value) {
+      menuHeight.value = menu.value.clientHeight;
+      chatLoading.value = false;
+    }
+  });
+};
+
+function chooseLang(lang) {
+  console.log("選擇語言:", lang);
+  selectLang.value = lang;
+  isLanguagePage.value = false;
+  locale.value = lang; // i18n locale
+  localStorage.setItem("lang", lang);
+
+  messages.value.push({
+    label: "text",
+    author: "ai",
+    body: t("prologue"),
+  });
+
+  messages.value.push({
+    label: "text",
+    author: "ai",
+    body: t("service"),
+  });
+
+  // 判斷語言修改 title
+  const language = localStorage.getItem("lang") || "zh-tw";
+  console.log("language", language);
+  if (language === "zh-tw") {
+    document.title = "New 台北101 AI智能客服";
+  } else if (language === "en-us") {
+    document.title = "New Taipei 101 AI Intelligent Customer Service";
+  }
+
+  setAd();
+  handleClick();
+
+  // 影片路徑
+  loadVideoSources();
+  // let lang = getLang();
+  let sources;
+  console.log("videoSources.value", videoSources.value);
+  if (language === "zh-tw") {
+    sources = videoSources.value;
+  } else if (language === "en-us") {
+    sources = videoSourcesEn.value;
+  }
+
+  const randomIndex = Math.floor(Math.random() * sources.length);
+  videoSrc.value = sources[randomIndex];
+  hideAnchorPrologue.value = true;
+  videoIndex.value = randomIndex + 1;
+
+  console.log("messages.value", messages.value);
+
+  setTimeout(() => {
+    showAd.value = true;
+    videoPlay();
+  }, 500);
+}
+
+let assignSubtype = ref("");
+let assignCategory = ref("");
+let assignCategoryIndex = ref(null);
+
+// 美食伴手禮
+let diningList = reactive([
+  {
+    value: "特色/高空餐廳",
+    text: "food_souvenirs_info.signature",
+  },
+  {
+    value: "輕食/CAFÉ",
+    text: "food_souvenirs_info.light",
+  },
+  {
+    value: "美食街",
+    text: "food_souvenirs_info.courts",
+  },
+  {
+    value: "伴手禮",
+    text: "food_souvenirs_info.souvenir",
+  },
+]);
+
+// 秘境花園觀景台
+let observationList = reactive([
+  {
+    value: "線上購票",
+    text: "observatory_info.tickets.title",
+  },
+  {
+    value: "參觀資訊",
+    text: "observatory_info.visitor.title",
+  },
+  // {
+  //   value: "實境景色",
+  //   text: "observatory_info.view",
+  // },
+  {
+    value: "前往秘境花園觀景台",
+    text: "observatory_info.garden",
+  },
+]);
+
+// 購物及優惠
+let shoppingList = reactive([
+  {
+    value: "購物品牌查詢",
+    text: "shopping_discounts_info.brands.title",
+  },
+  {
+    value: "國際貴賓卡專屬禮遇",
+    text: "shopping_discounts_info.tourist_card.title",
+  },
+]);
+
+// 購物品牌查詢
+let brandList = reactive([
+  {
+    value: "國際珠寶腕錶",
+    text: "shopping_discounts_info.brands.jewely",
+  },
+  {
+    value: "國際精品",
+    text: "shopping_discounts_info.brands.boutique",
+  },
+  {
+    value: "美妝保養品",
+    text: "shopping_discounts_info.brands.skincare",
+  },
+  {
+    value: "流行服飾",
+    text: "shopping_discounts_info.brands.apparel",
+  },
+  {
+    value: "生活居家/3C",
+    text: "shopping_discounts_info.brands.lifestyle",
+  },
+  {
+    value: "文化創意",
+    text: "shopping_discounts_info.brands.cultural",
+  },
+  {
+    value: "特色品牌",
+    text: "shopping_discounts_info.brands.signature",
+  },
+  {
+    value: "館外店家",
+    text: "shopping_discounts_info.brands.outside",
+  },
+]);
+
+// 服務資訊
+let serviceList = reactive([
+  {
+    title: "service_info.business_hours",
+    depiction: "service_info.business_hours_1",
+  },
+  {
+    title: "service_info.tax_refund",
+    depiction: "service_info.tax_refund_1",
+  },
+  {
+    title: "service_info.toilets",
+    depiction: "service_info.toilets_1",
+  },
+  {
+    title: "service_info.charge",
+    depiction: "service_info.charge_1",
+  },
+  // {
+  //   title: "AI 天燈",
+  //   depiction: "https://cmm.ai/101-postcard/#/skylanternhome",
+  // },
+]);
+
+// 定位點
+let locationList = reactive([
+  {
+    location: "B1 鼎泰豐", // 編號 1
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "鼎泰豐",
+        text: "din_tai_fung",
+      },
+      {
+        value: "捷運",
+        text: "metro_station",
+      },
+      {
+        value: "超市",
+        text: "supermarket",
+      },
+      {
+        value: "美食街",
+        text: "food_court",
+      },
+      {
+        value: "退稅/外幣兌換櫃台",
+        text: "tax_refund_currency_exchange",
+      },
+      {
+        value: "計程車乘車處",
+        text: "taxi_station",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+      {
+        value: "客服(停車折抵)",
+        text: "service_counter_parking_discount",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "伴手禮區",
+        text: "snackable_souvenirs",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "置物櫃",
+        text: "items_and_luggage_storage",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "鼎泰豐",
+    //   "捷運",
+    //   "超市",
+    //   "美食街",
+    //   "退稅/外幣兌換櫃台",
+    //   "計程車乘車處",
+    //   "無障礙廁所",
+    //   "客服(停車折抵)",
+    //   "哺乳室",
+    //   "飲水機",
+    //   "伴手禮區",
+    //   "廁所",
+    //   "置物櫃",
+    // ],
+  },
+  {
+    location: "B1 中環", // 編號 2
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "美食街",
+        text: "food_court",
+      },
+      {
+        value: "伴手禮區",
+        text: "snackable_souvenirs",
+      },
+      {
+        value: "鼎泰豐",
+        text: "din_tai_fung",
+      },
+      {
+        value: "捷運",
+        text: "metro_station",
+      },
+      {
+        value: "超市",
+        text: "supermarket",
+      },
+      {
+        value: "計程車乘車處",
+        text: "taxi_station",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "美食街",
+    //   "伴手禮區",
+    //   "鼎泰豐",
+    //   "捷運",
+    //   "超市",
+    //   "計程車乘車處",
+    //   "無障礙廁所",
+    // ],
+  },
+  {
+    location: "B1 2號電梯", // 編號 3
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "客服(退稅櫃台/外幣兌換)",
+        text: "tax_refund_currency_exchange",
+      },
+      {
+        value: "計程車乘車處",
+        text: "taxi_station",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+      {
+        value: "超市",
+        text: "supermarket",
+      },
+      {
+        value: "鼎泰豐",
+        text: "din_tai_fung",
+      },
+      {
+        value: "捷運",
+        text: "metro_station",
+      },
+      {
+        value: "伴手禮區",
+        text: "snackable_souvenirs",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "置物櫃",
+        text: "items_and_luggage_storage",
+      },
+      {
+        value: "美食街",
+        text: "food_court",
+      },
+      {
+        value: "客服(停車折抵)",
+        text: "service_counter_parking_discount",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "客服(退稅櫃台/外幣兌換)",
+    //   "計程車乘車處",
+    //   "無障礙廁所",
+    //   "超市",
+    //   "鼎泰豐",
+    //   "捷運",
+    //   "伴手禮區",
+    //   "廁所",
+    //   "置物櫃",
+    //   "美食街",
+    //   "客服(停車折抵)",
+    //   "哺乳室",
+    //   "飲水機",
+    //   "廁所",
+    // ],
+  },
+  {
+    location: "B1 4號電梯", // 編號 4
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "置物櫃",
+        text: "items_and_luggage_storage",
+      },
+      {
+        value: "客服(停車折抵)",
+        text: "service_counter_parking_discount",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "退稅/外幣兌換櫃台",
+        text: "tax_refund_currency_exchange",
+      },
+      {
+        value: "計程車乘車處",
+        text: "taxi_station",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+      {
+        value: "伴手禮區",
+        text: "snackable_souvenirs",
+      },
+      {
+        value: "捷運",
+        text: "metro_station",
+      },
+      {
+        value: "鼎泰豐",
+        text: "din_tai_fung",
+      },
+      {
+        value: "美食街",
+        text: "food_court",
+      },
+      {
+        value: "超市",
+        text: "supermarket",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "置物櫃",
+    //   "客服(停車折抵)",
+    //   "哺乳室",
+    //   "飲水機",
+    //   "退稅/外幣兌換櫃台",
+    //   "計程車乘車處",
+    //   "無障礙廁所",
+    //   "伴手禮區",
+    //   "捷運",
+    //   "鼎泰豐",
+    //   "美食街",
+    //   "超市",
+    // ],
+  },
+  {
+    location: "B1 3號電梯", // 編號 5
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+      {
+        value: "計程車乘車處",
+        text: "taxi_station",
+      },
+      {
+        value: "客服(退稅櫃台/外幣兌換)",
+        text: "tax_refund_currency_exchange",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+      {
+        value: "置物櫃",
+        text: "items_and_luggage_storage",
+      },
+      {
+        value: "客服(停車折抵)",
+        text: "service_counter_parking_discount",
+      },
+      {
+        value: "美食街",
+        text: "food_court",
+      },
+      {
+        value: "伴手禮區",
+        text: "snackable_souvenirs",
+      },
+      {
+        value: "鼎泰豐",
+        text: "din_tai_fung",
+      },
+      {
+        value: "捷運",
+        text: "metro_station",
+      },
+      {
+        value: "超市",
+        text: "supermarket",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "無障礙廁所",
+    //   "計程車乘車處",
+    //   "客服(退稅櫃台/外幣兌換)",
+    //   "飲水機",
+    //   "哺乳室",
+    //   "置物櫃",
+    //   "客服(停車折抵)",
+    //   "美食街",
+    //   "伴手禮區",
+    //   "鼎泰豐",
+    //   "捷運",
+    //   "超市",
+    // ],
+  },
+  {
+    location: "1F 信義環", // 編號 6
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "置物櫃",
+        text: "items_and_luggage_storage",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+      {
+        value: "客服(停車折抵)",
+        text: "service_counter_parking_discount",
+      },
+      {
+        value: "退稅/外幣兌換櫃台",
+        text: "tax_refund_currency_exchange",
+      },
+      {
+        value: "伴手禮區",
+        text: "snackable_souvenirs",
+      },
+      {
+        value: "美食街",
+        text: "food_court",
+      },
+      {
+        value: "捷運",
+        text: "metro_station",
+      },
+      {
+        value: "鼎泰豐",
+        text: "din_tai_fung",
+      },
+      {
+        value: "超市",
+        text: "supermarket",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "ATM",
+    //   "置物櫃",
+    //   "飲水機",
+    //   "無障礙廁所",
+    //   "哺乳室",
+    //   "客服(停車折抵)",
+    //   "退稅/外幣兌換櫃台",
+    //   "伴手禮區",
+    //   "美食街",
+    //   "捷運",
+    //   "鼎泰豐",
+    //   "超市",
+    // ],
+  },
+  {
+    location: "1F 3號電梯", // 編號 7
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "置物櫃",
+        text: "items_and_luggage_storage",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+      {
+        value: "客服(停車折抵)",
+        text: "service_counter_parking_discount",
+      },
+      {
+        value: "退稅/外幣兌換櫃台",
+        text: "tax_refund_currency_exchange",
+      },
+      {
+        value: "計程車乘車處",
+        text: "taxi_station",
+      },
+      {
+        value: "美食街",
+        text: "food_court",
+      },
+      {
+        value: "超市",
+        text: "supermarket",
+      },
+      {
+        value: "捷運",
+        text: "metro_station",
+      },
+      {
+        value: "伴手禮區",
+        text: "snackable_souvenirs",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "置物櫃",
+    //   "飲水機",
+    //   "無障礙廁所",
+    //   "哺乳室",
+    //   "客服(停車折抵)",
+    //   "退稅/外幣兌換櫃台",
+    //   "計程車乘車處",
+    //   "美食街",
+    //   "超市",
+    //   "捷運",
+    //   "伴手禮區",
+    // ],
+  },
+  {
+    location: "2F 3號電梯", // 編號 8
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "觀景台售票處",
+        text: "observatory_ticket_office",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "置物櫃",
+        text: "items_and_luggage_storage",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "無障礙廁所",
+        text: "accessible_facilities",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+      {
+        value: "計程車乘車處",
+        text: "taxi_station",
+      },
+      {
+        value: "退稅/外幣兌換櫃台",
+        text: "tax_refund_currency_exchange",
+      },
+      {
+        value: "客服(停車折抵)",
+        text: "service_counter_parking_discount",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "觀景台售票處",
+    //   "廁所",
+    //   "置物櫃",
+    //   "飲水機",
+    //   "無障礙廁所",
+    //   "哺乳室",
+    //   "計程車乘車處",
+    //   "退稅/外幣兌換櫃台",
+    //   "客服(停車折抵)",
+    // ],
+  },
+  {
+    location: "89F 觀景台", // 編號 9
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "客服",
+        text: "service_counter",
+      },
+      {
+        value: "91F戶外區",
+        text: "91f_outdoor_area",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "101F排隊處",
+        text: "101f_queue_area",
+      },
+    ],
+    // navigation: [
+    //   "景觀餐廳櫃台",
+    //   "ATM",
+    //   "客服",
+    //   "91F戶外區",
+    //   "廁所",
+    //   "101F排隊處",
+    // ],
+  },
+  {
+    location: "88F 觀景台", // 編號 10
+    navigation: [
+      {
+        value: "景觀餐廳櫃台",
+        text: "high_rise_restaurant_reception",
+      },
+      {
+        value: "ATM",
+        text: "atm",
+      },
+      {
+        value: "廁所",
+        text: "restroom",
+      },
+      {
+        value: "飲水機",
+        text: "ice_warmed_water_dispenser",
+      },
+      {
+        value: "哺乳室",
+        text: "nursing_room",
+      },
+    ],
+    // navigation: ["景觀餐廳櫃台", "ATM", "廁所", "飲水機", "哺乳室"],
+  },
+]);
+
+// 平面圖
+let mapList = reactive([
+  {
+    title: "B1 平面圖",
+  },
+  {
+    title: "1F 平面圖",
+  },
+  {
+    title: "2F 平面圖",
+  },
+]);
+
+let assignLocation = ref("當前位置"); // 指定定位點
+let assignNavigationList = ref(null); // 指定導覽點列表
+// let assignLocationFloor = ref(null); // 指定定位點樓層
+// let assignNavigation = ref(null); // 指定導覽點
+// let navigationBtn = reactive(["B1", "1F", "2F", "4F", "5F", "89F", "91F"]);
+
+let arVideo = ref(null);
+let arVideoDialog = ref(false);
+
+// 取得 AR 導覽影片
+async function getArviews(route, text, type = "") {
+  let url;
+  let lang = getLang();
+  console.log("text >>>", text);
+
+  if (type === "garden") {
+    console.log("route", route);
+    let start;
+    // if (route === "B1 鼎泰豐旁 數位屏幕") {
+    //   start = "B1 鼎泰豐";
+    // } else if (route === "B1 松智藍梯梯廳") {
+    //   start = "B1 3號電梯";
+    // }
+
+    switch (route) {
+      case "B1 鼎泰豐旁 數位屏幕":
+        start = "B1 鼎泰豐";
+        break;
+      case "B1 中環WOW屏幕":
+        start = "B1 中環";
+        break;
+      case "B1 信義橘梯梯廳":
+        start = "B1 2號電梯";
+        break;
+      case "B1 信義綠梯梯廳":
+        start = "B1 4號電梯";
+        break;
+      case "B1 松智藍梯梯廳":
+        start = "B1 3號電梯";
+        break;
+      case "1F 信義綠梯出入口":
+        start = "1F 信義環";
+        break;
+      case "1F 松智梯廳":
+        start = "1F 3號電梯";
+        break;
+      case "2F 松智藍梯梯廳":
+        start = "2F 3號電梯";
+        break;
+      default:
+        break;
+    }
+
+    url = `https://cmm.ai:9101/arviews?language=${lang}&start=${start}&end=觀景台售票處`;
+  } else {
+    url = `https://cmm.ai:9101/arviews?language=${lang}&start=${assignLocation.value}&end=${route}`;
+  }
+
+  console.log("url", url);
+
+  messages.value.push({
+    label: "text",
+    author: "ai",
+    body: text,
+  });
+
+  try {
+    const response = await axios.get(url);
+    console.log("AR 導覽影片", response);
+
+    messages.value.push({
+      label: "ar_views",
+      author: "ai",
+      body: response.data,
+    });
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+// 取得定位點 & 導覽點
+function changeLocation(item) {
+  assignLocation.value = item;
+
+  console.log("定位點:", item);
+
+  let assign = locationList.filter((e) => e.location === item);
+  console.log("assign", assign);
+
+  assignNavigationList.value = assign[0].navigation;
+
+  // assignLocation.value = item.location;
+  // assignNavigationList.value = item.navigation;
+
+  console.log("assignNavigationList.value", assignNavigationList.value);
+
+  messages.value.push({
+    label: "text",
+    author: "ai",
+    body: t("select_navigation_location"),
+  });
+
+  messages.value.push({
+    label: "navigation",
+    author: "ai",
+    body: assignNavigationList.value,
+  });
+}
+
+// 顯示平面圖
+function assignMapImg(item) {
+  console.log("顯示平面圖 item", item);
+  let name;
+  if (item.title === "B1 平面圖") {
+    name = "All_b1";
+  } else if (item.title === "1F 平面圖") {
+    name = "All_1";
+  } else if (item.title === "2F 平面圖") {
+    name = "All_2";
+  }
+
+  messages.value.push({
+    label: "map_img",
+    author: "ai",
+    body: name,
+  });
+}
+
+// 動態引入視頻文件
+const videoSources = ref([]); // 開場白影片(中)
+const videoSourcesEn = ref([]); // 開場白影片(英)
+const videoMuteSources = ref([]); // 點頭影片(靜音)
+const videoSpeakSources = ref([]); // 動嘴型影片
+
+const loadVideoSources = async () => {
+  // 本地端影片路徑
+  // const start1 = await import("@/assets/video/start_1.mp4");
+  // const start2 = await import("@/assets/video/start_2.mp4");
+  // const mute_1 = await import("@/assets/video/mute_1.mp4");
+  // const mute_2 = await import("@/assets/video/mute_2.mp4");
+  // const speak_1 = await import("@/assets/video/speak_1.mp4");
+  // const speak_2 = await import("@/assets/video/speak_2.mp4");
+  // videoSpeakSources.value = [speak_1.default, speak_2.default];
+
+  videoSources.value = [
+    // "https://cmm.ai/101-ai-chatbot-new/video/start_1.mp4",
+    "https://cmm.ai/101-ai-chatbot-new/video/start_2.mp4",
+  ];
+  videoSourcesEn.value = [
+    // "https://cmm.ai/101-ai-chatbot-new/video/start_en_1.mp4",
+    "https://cmm.ai/101-ai-chatbot-new/video/start_en_2.mp4",
+  ];
+  videoMuteSources.value = [
+    // "https://cmm.ai/101-ai-chatbot-new/video/mute_1.mp4",
+    "https://cmm.ai/101-ai-chatbot-new/video/mute_2.mp4",
+  ];
+  videoSpeakSources.value = [
+    // "https://cmm.ai/101-ai-chatbot-new/video/speak_1.mp4",
+    "https://cmm.ai/101-ai-chatbot-new/video/speak_2.mp4",
+  ];
+};
+
+let videoSrc = ref("");
+let hideAnchorPrologue = ref(false); // 顯示開場白 or 點頭影片
+let videoIndex = ref(null); // 影片編號
+
+// 選擇類別
+async function selectCategory(value, index) {
+  assignCategory.value = value;
+  assignCategoryIndex.value = index;
+
+  if (value === "叫出真人客服") {
+    showAnchor.value = true;
+
+    if (currentAudio.value && isAudioPlaying.value) {
+      handleVoice("pause"); // 暫停音訊
+    }
+
+    // 隨機取得影片路徑
+    await loadVideoSources();
+
+    // 主播開場白只顯示一次
+    if (!hideAnchorPrologue.value) {
+      let lang = getLang();
+      let sources;
+
+      if (lang === "ch") {
+        sources = videoSources.value;
+      } else if (lang === "en") {
+        sources = videoSourcesEn.value;
+      }
+
+      const randomIndex = Math.floor(Math.random() * sources.length);
+      videoSrc.value = sources[randomIndex];
+      hideAnchorPrologue.value = true;
+      videoIndex.value = randomIndex + 1;
+
+      // const randomIndex = Math.floor(Math.random() * videoSources.value.length);
+      // videoSrc.value = videoSources.value[randomIndex];
+      // hideAnchorPrologue.value = true;
+      // videoIndex.value = randomIndex + 1;
+    } else {
+      const randomIndex = Math.floor(
+        Math.random() * videoMuteSources.value.length
+      );
+      videoSrc.value = videoMuteSources.value[randomIndex];
+      videoIndex.value = randomIndex + 1;
+    }
+
+    // 播放影片
+    setTimeout(() => {
+      videoPlay();
+    }, 0);
+
+    // const randomIndex = Math.floor(Math.random() * videoSources.value.length);
+    // videoSrc.value = videoSources.value[randomIndex];
+  }
+  //  else if (value === "隱藏真人客服") {
+  //   showAnchor.value = false;
+  //   video.value.pause();
+  //   menuList[0][0].text = "customer_show";
+  //   menuList[0][0].value = "叫出真人客服";
+  // }
+  else if (value === "秘境花園觀景台") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("observatory_info.tickets.purchase"),
+    });
+
+    messages.value.push({
+      label: "observation_deck",
+      author: "ai",
+      body: observationList,
+    });
+  } else if (value === "位置導引") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("qr_code_scan_prompt"),
+    });
+
+    // messages.value.push({
+    //   label: "map_img",
+    //   author: "ai",
+    //   body: mapList,
+    // });
+
+    messages.value.push({
+      label: "location",
+      author: "ai",
+      body: locationList,
+    });
+  } else if (value === "美食/伴手禮") {
+    // getAd("美食伴手禮");
+
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("food_souvenirs_info.searching"),
+    });
+
+    messages.value.push({
+      label: "dining",
+      author: "ai",
+      body: diningList,
+    });
+  } else if (value === "購物及優惠") {
+    // getAd("購物及優惠");
+
+    messages.value.push({
+      label: "shopping",
+      author: "ai",
+      body: shoppingList,
+    });
+  } else if (value === "服務資訊") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("service_info.inquiry_prompt"),
+    });
+
+    messages.value.push({
+      label: "service",
+      author: "ai",
+      body: serviceList,
+    });
+  }
+
+  // 關閉選單
+  hideMenu.value = true;
+  showInput.value = true;
+}
+
+let secondaryAd = ref({}); // 隨機廣告
+let secondaryAdShow = ref(false);
+
+// 取得隨機廣告 (美食伴手禮/購物及優惠)
+async function getAd(type) {
+  let lang = getLang();
+  let url = `https://cmm.ai:9101/ad/${type}?language=${lang}`;
+
+  try {
+    const response = await axios.get(url);
+    console.log("response", response);
+    secondaryAd.value = response.data.data;
+    // secondaryAdShow.value = true; // 取消蓋版廣告
+
+    messages.value.push({
+      label: "brand",
+      author: "ai",
+      body: [
+        {
+          info: response.data.data,
+        },
+      ],
+    });
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+// 服務資訊回覆
+function handleService(title, depiction) {
+  messages.value.push({
+    label: "text",
+    author: "user",
+    body: title,
+  });
+
+  if (title !== "AI 天燈") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: depiction,
+    });
+  } else {
+    window.location.href = depiction;
+    // window.open(depiction, "_blank"); // 另開天燈頁面
+  }
+}
+
+// 參觀資訊回覆
+function handleVisit(title, depiction) {
+  messages.value.push({
+    label: "text",
+    author: "user",
+    body: title,
+  });
+
+  messages.value.push({
+    label: "text",
+    author: "ai",
+    body: depiction,
+  });
+}
+
+// 計算使用次數
+async function handleClick() {
+  let url = "https://cmm.ai:9101/click";
+  try {
+    const response = await axios.get(url);
+    console.log("Click", response);
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+let langList = reactive([
+  {
+    lang: "中文",
+    value: "zh-tw",
+  },
+  {
+    lang: "English",
+    value: "en-us",
+  },
+  // {
+  //   lang: "日本語",
+  //   value: "ja-jp",
+  // },
+  // {
+  //   lang: "한국어",
+  //   value: "ko-kr",
+  // },
+]);
+
+const btnList = reactive([
+  {
+    title: "observation_deck",
+    value: "秘境花園觀景台",
+  },
+  {
+    title: "location_guide",
+    value: "位置導引",
+  },
+  {
+    title: "food_souvenirs",
+    value: "美食伴手禮",
+  },
+  {
+    title: "shopping_discounts",
+    value: "購物及優惠",
+  },
+  {
+    title: "service_information",
+    value: "服務資訊",
+  },
+]);
+
+function getImageUrl(name) {
+  return new URL(`../assets/img/icon/${name}`, import.meta.url).href;
+}
+
+let showAnchor = ref(false); // AI 主播影片
+let videoContent = ref(null); // 主播影片區塊
+
+// 動態更改主播區塊位置
+// watch(showAnchor, () => {
+//   console.log("showAnchor", showAnchor.value);
+//   nextTick(() => {
+//     changeAnchorTop();
+//   });
+// });
+
+// watch(hideMenu, () => {
+//   nextTick(() => {
+//     changeAnchorTop();
+//   });
+// });
+
+// function changeAnchorTop() {
+//   const menuOffsetTop = menu.value.offsetTop;
+//   videoContent.value.style.top = `${menuOffsetTop - 170}px`;
+// }
+
+const menuList = reactive([
+  [
+    {
+      imgSrc: "素材-05.png",
+      text: "customer_show",
+      value: "叫出真人客服",
+    },
+    { imgSrc: "素材-06.png", text: "service_information", value: "服務資訊" },
+    { imgSrc: "素材-07.png", text: "shopping_discounts", value: "購物及優惠" },
+  ],
+  [
+    {
+      imgSrc: "素材-08.png",
+      text: "observation_deck",
+      value: "秘境花園觀景台",
+    },
+    { imgSrc: "素材-09.png", text: "food_souvenirs", value: "美食/伴手禮" },
+    { imgSrc: "素材-10.png", text: "location_guide", value: "位置導引" },
+  ],
+]);
+
+// 美食伴手禮 or 購物及優惠類別篩選
+async function findBrand(value) {
+  console.log("findBrand", value);
+
+  if (value === "館外店家") {
+    value = "館外";
+  }
+
+  let lang = getLang();
+  let url = `https://cmm.ai:9101/find_brand?keyword=${value}&language=${lang}`;
+
+  try {
+    const response = await axios.get(url);
+    console.log("response", response);
+
+    // response.data.data.map(
+    //   (item) => (item.info.tags = JSON.parse(item.info.tags))
+    // );
+
+    messages.value.push({
+      label: "brand",
+      author: "ai",
+      body: response.data.data,
+    });
+
+    assignCategoryIndex.value = null;
+    assignCategory.value = "";
+
+    // messages.value.push({
+    //   label: "text",
+    //   author: "ai",
+    //   body: "請選擇問題類型:",
+    // });
+
+    // messages.value.push({
+    //   label: "btn-list",
+    //   author: "ai",
+    //   body: "",
+    // });
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+// 處理電話格式
+// function getPhoneNumber(phoneString) {
+//   const parts = phoneString.split("\n");
+//   return parts[1];
+// }
+
+// 取得線上購票
+async function getStaticTickets(type) {
+  let url = `https://cmm.ai:9101/static_tickets?is_Chinese=${type}`;
+
+  let info = {
+    buttonList: [], // 按鈕
+    ticketList: [], // 票券
+  };
+
+  try {
+    const response = await axios.get(url);
+    console.log("線上購票", response.data.result);
+
+    response.data.result.map((item) => info.ticketList.push(item));
+    console.log("info", info);
+
+    messages.value.push({
+      label: "ticket",
+      author: "ai",
+      body: info,
+    });
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+// 秘境花園觀景台對話
+function handleObservationDialog(value) {
+  console.log("value", value);
+
+  if (value === "線上購票") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("observatory_info.tickets.id_card"),
+    });
+
+    messages.value.push({
+      label: "check",
+      author: "ai",
+      body: [
+        {
+          value: "是",
+          text: "observatory_info.tickets.yes",
+        },
+        {
+          value: "否",
+          text: "observatory_info.tickets.no",
+        },
+      ],
+    });
+  } else if (value === "是" || value === "否") {
+    if (value === "是") {
+      getStaticTickets("1");
+    } else if (value === "否") {
+      getStaticTickets("0");
+    }
+
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("observatory_info.tickets.ticket_type"),
+    });
+  } else if (value === "參觀資訊") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("observatory_info.visitor.question"),
+    });
+
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("observatory_info.visitor.faq"),
+    });
+
+    messages.value.push({
+      label: "visit",
+      author: "ai",
+      body: visitList,
+    });
+  } else if (value === "實境景色") {
+    // window.location.href = "https://www.youtube.com/watch?v=RVV00FZbeH0";
+    window.open("https://www.youtube.com/watch?v=RVV00FZbeH0", "_blank"); // 另開頁面
+  } else if (value === "前往秘境花園觀景台") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("select_location"),
+    });
+
+    messages.value.push({
+      label: "garden_route",
+      author: "ai",
+      body: gardenRouteList,
+      // body: [
+      //   "B1 鼎泰豐旁 數位屏幕",
+      //   "B1 中環WOW屏幕",
+      //   "B1 信義橘梯梯廳",
+      //   "B1 信義綠梯梯廳",
+      //   "B1 松智藍梯梯廳",
+      //   "1F 信義綠梯出入口",
+      //   "1F 松智梯廳",
+      //   "2F 松智藍梯梯廳",
+      // ],
+    });
+  }
+}
+
+let gardenRouteList = reactive([
+  {
+    value: "B1 鼎泰豐旁 數位屏幕",
+    text: "location.b1_din_tai_fung",
+  },
+  {
+    value: "B1 中環WOW屏幕",
+    text: "location.b1_centre",
+  },
+  {
+    value: "B1 信義橘梯梯廳",
+    text: "location.b1_elevator_no_2",
+  },
+  {
+    value: "B1 信義綠梯梯廳",
+    text: "location.b1_elevator_no_4",
+  },
+  {
+    value: "B1 松智藍梯梯廳",
+    text: "location.b1_elevator_no_3",
+  },
+  {
+    value: "1F 信義綠梯出入口",
+    text: "location.1f_south_xinyi",
+  },
+  {
+    value: "1F 松智梯廳",
+    text: "location.1f_elevator_no_3",
+  },
+  {
+    value: "2F 松智藍梯梯廳",
+    text: "location.2f_elevator_no_3",
+  },
+]);
+
+// 購物及優惠對話
+function handleShoppingDialog(value) {
+  console.log("value", value);
+
+  if (value === "購物品牌查詢") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: "請選擇品牌類別",
+    });
+
+    messages.value.push({
+      label: "shopping_brand",
+      author: "ai",
+      body: brandList,
+    });
+  } else if (value === "國際貴賓卡專屬禮遇") {
+    messages.value.push({
+      label: "text",
+      author: "user",
+      body: "國際貴賓卡專屬禮遇",
+    });
+
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("shopping_discounts_info.tourist_card.content"),
+    });
+  } else if (value === "參觀資訊") {
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("observatory_info.visitor.question"),
+    });
+
+    messages.value.push({
+      label: "text",
+      author: "ai",
+      body: t("observatory_info.visitor.faq"),
+    });
+
+    messages.value.push({
+      label: "visit",
+      author: "ai",
+      body: visitList,
+    });
+  }
+}
+
+let visitList = reactive([
+  {
+    title: "observatory_info.visitor.events_q",
+    depiction: "observatory_info.visitor.events_a",
+  },
+  {
+    title: "observatory_info.visitor.ball_q",
+    depiction: "observatory_info.visitor.ball_a",
+  },
+  {
+    title: "observatory_info.visitor.accessible_q",
+    depiction: "observatory_info.visitor.accessible_a",
+  },
+]);
+
+// 判斷價格是否包含 $ 符號
+function formatPrice(price) {
+  if (price === "") {
+    return "";
+  } else {
+    if (price.includes("$")) {
+      return price;
+    } else {
+      return `$${price}`;
+    }
+  }
+}
+
+let currentAudio = ref(null); // 當前音訊
+let audioDuration = ref(null); // 音訊秒數
+
+// 文字轉語音 (TTS)
+async function handleTTS(message) {
+  console.log("handleTTS", message);
+
+  let audioLang; // 音訊語言
+  let lang = localStorage.getItem("lang");
+  console.log("lang", lang);
+
+  switch (lang) {
+    case "zh-tw":
+      audioLang = "cmn-TW";
+      break;
+    case "en-us":
+      audioLang = "en-US";
+      break;
+    case "ja-jp":
+      audioLang = "ja-JP";
+      break;
+    case "ko-kr":
+      audioLang = "ko-KR";
+      break;
+    default:
+      break;
+  }
+
+  // let url = `https://cmm.ai:9001/ttsTry/tts_try?message=${message}&type=101`;
+  let url = `https://cmm.ai:9001/gcp/text-to-speech?language_code=${audioLang}&gender=female`;
+
+  const formData = new FormData();
+  formData.append("text", message);
+
+  try {
+    const response = await axios.post(url, formData, { responseType: "blob" });
+    console.log("TTS response", response);
+
+    const blob = new Blob([response.data], { type: "audio/mp3" });
+    const audioUrl = URL.createObjectURL(blob);
+    console.log("audioUrl", audioUrl);
+
+    cutVideo(); // 剪接影片
+
+    // 取得 mp3 音訊秒數
+    // audio.addEventListener("loadedmetadata", function () {
+    //   audioDuration.value = audio.duration;
+    //   cutVideo();
+    // });
+
+    // 暫停當前音訊
+    if (currentAudio.value) {
+      currentAudio.value.pause();
+      currentAudio.value.currentTime = 0;
+    }
+
+    // 播放音檔
+    currentAudio.value = new Audio(audioUrl);
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+let videoLoading = ref(false);
+let isAudioPlaying = ref(false); // 音訊播放狀態
+
+// 取得語音回覆 mp4
+async function cutVideo() {
+  videoSrc.value = videoSpeakSources.value[videoIndex.value - 1];
+  video.value.load(); // 重新讀取影片
+
+  // 影片和音訊加載完成後播放
+  video.value.oncanplay = () => {
+    setTimeout(() => {
+      // 監聽音訊播放結束
+      currentAudio.value.addEventListener("ended", onAudioEnded);
+
+      // 監聽音訊播放狀態
+      currentAudio.value.addEventListener("play", onAudioPlay);
+      currentAudio.value.addEventListener("pause", onAudioPause);
+
+      video.value.play(); // 播放影片
+      currentAudio.value.currentTime = 0; // 重置時間
+      currentAudio.value.play(); // 播放音訊
+      isVideoPause.value = true;
+      videoLoading.value = false;
+    }, 500);
+  };
+}
+
+// 音訊結束後暫停影片播放
+const onAudioEnded = () => {
+  video.value.pause();
+};
+
+// 判斷音訊是否為播放狀態
+const onAudioPlay = () => {
+  isAudioPlaying.value = true;
+  console.log("isAudioPlaying.value", isAudioPlaying.value);
+};
+
+const onAudioPause = () => {
+  isAudioPlaying.value = false;
+  isVideoPause.value = false;
+  console.log("isAudioPlaying.value", isAudioPlaying.value);
+};
+
+const audioURL = ref(null);
+const audioFile = ref(null); // 音訊檔案
+
+let recordTime = ref(0); // 錄音時間
+let isRecording = ref(false); // 錄音狀態
+let timer;
+
+// 語音轉文字
+async function handleAudioToText() {
+  isRecording.value = false;
+  let audioLang; // 音訊語言
+  let lang = localStorage.getItem("lang");
+  console.log("lang", lang);
+
+  switch (lang) {
+    case "zh-tw":
+      audioLang = "cmn-Hant-TW";
+      break;
+    case "en-us":
+      audioLang = "en-US";
+      break;
+    case "ja-jp":
+      audioLang = "ja-JP";
+      break;
+    case "ko-kr":
+      audioLang = "ko-KR";
+      break;
+    default:
+      break;
+  }
+
+  // let url = `http://172.104.93.163:9880/whisper/${audioLang}/`;
+  // let url = `https://cmm.ai:9001/whisper/${audioLang}/`;
+  let url = `https://cmm.ai:9001/gcp/speech-to-text?language_code=${audioLang}`;
+
+  const formData = new FormData();
+  formData.append("file", audioFile.value);
+
+  try {
+    console.log("audioFile.value", audioFile.value);
+    const response = await axios.post(url, formData);
+    console.log("語音轉文字 response", response);
+    // showAnchor.value = false; // 關閉主播視窗
+
+    userMessage.value = response.data[0];
+    // handleTTS(userMessage.value); // 取得語音回覆
+
+    if (response.data[0] && response.data[0] !== "") {
+      sendMessage(); // 傳送使用者訊息
+    } else {
+      if (showAnchor.value) {
+        alert("語音辨識有誤,請重新錄製。");
+        videoLoading.value = false;
+        return;
+      } else {
+        messages.value.push({
+          label: "text",
+          author: "user",
+          body: "語音辨識有誤,請重新錄製。",
+        });
+      }
+    }
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+// 語音轉文字 (使用 recorder-core 錄音)
+let rec, wave;
+
+// 調用 open 請求錄音權限
+let recOpen = function (success) {
+  rec = Recorder({
+    type: "mp3",
+    sampleRate: 16000,
+    bitRate: 16,
+    onProcess: function (
+      buffers,
+      powerLevel,
+      bufferDuration,
+      bufferSampleRate,
+      newBufferIdx,
+      asyncEnd
+    ) {
+      wave &&
+        wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
+    },
+  });
+
+  rec.open(
+    function () {
+      if (Recorder.WaveView) wave = Recorder.WaveView({ elem: ".recwave" });
+      success && success();
+    },
+    function (msg, isUserNotAllow) {
+      // 使用者未授權或不支援
+      console.log((isUserNotAllow ? "UserNotAllow," : "") + "無法錄音:" + msg);
+    }
+  );
+};
+
+/** 開始錄音 **/
+function recStart() {
+  togglePause("pause"); // 暫停影片音訊
+  // 需先呼叫 recOpen() 開啟錄音後才能調用 start、stop 方法
+  console.log("開始錄音");
+
+  recOpen(function () {
+    isRecording.value = true;
+
+    // 開始計時
+    timer = setInterval(() => {
+      recordTime.value += 1;
+    }, 1000);
+
+    rec.start();
+  });
+}
+
+/** 結束錄音 **/
+function recStop() {
+  videoLoading.value = true;
+  rec.stop(
+    function (blob, duration) {
+      // 利用 URL 產生本地檔案位址,不用時需要 revokeObjectURL
+      let localUrl = (window.URL || webkitURL).createObjectURL(blob); // 該 url 只能本地端使用 (例如給 audio.src 進行播放,或是給 a.href download 進行下載)
+      console.log(blob, localUrl, "時長:" + duration + "ms");
+      // rec.close(); // 釋放錄音資源 (若不釋放系統或瀏覽器將持續提示在錄音中)
+      // rec = null;
+
+      // 將 Blob 轉換為 File 對象
+      audioFile.value = new File([blob], "recording.mp3", {
+        type: "audio/mp3",
+      });
+
+      console.log("audioFile", audioFile.value);
+
+      // 自動播放(測試用)
+      // const audio = document.createElement("audio");
+      // audio.src = localUrl;
+      // audio.autoplay = true;
+      // audio
+      //   .play()
+      //   .then(() => {
+      //     console.log("Audio is playing automatically.");
+      //   })
+      //   .catch((error) => {
+      //     console.error("Error playing audio:", error);
+      //   });
+
+      rec.close(); // 釋放錄音資源 (若不釋放系統或瀏覽器將持續提示在錄音中)
+      rec = null;
+
+      if (recordTime.value !== 0) {
+        handleAudioToText(); // 語音轉文字
+      } else {
+        isRecording.value = false;
+      }
+
+      clearInterval(timer); // 清空計時秒數
+      recordTime.value = 0;
+    },
+    function (msg) {
+      console.log("錄音失敗:" + msg);
+      rec.close(); // 可以透過 stop 方法的第 3 個參數來自動呼叫 close
+      rec = null;
+    }
+  );
+}
+
+let videoCacheData = ref({});
+
+async function getVideoCache(messages) {
+  let url = `https://cmm.ai:9101/video_cache?client_message=${messages}`;
+
+  try {
+    const response = await axios.post(url);
+    console.log("response", response);
+    console.log("response.status", response.status);
+    if (response.data.state === 200) {
+      videoCacheData.value = response.data.message[0];
+      console.log("videoCacheData.value", videoCacheData.value);
+      return true;
+    } else {
+      return false;
+    }
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+// 播放 Video Cache
+function handleVideoCache() {
+  console.log("播放 Video Cache", videoCacheData.value);
+  // AI 客服回傳訊息
+  messages.value.push({
+    label: "text",
+    author: "ai",
+    body: videoCacheData.value.answer,
+  });
+
+  // 播放 Cache 影片
+  videoSrc.value = `https://cmm.ai:9101${videoCacheData.value.video_url}`;
+  video.value.load();
+
+  // 清空音訊
+  if (currentAudio.value) {
+    currentAudio.value.pause();
+    currentAudio.value.currentTime = 0;
+    currentAudio.value = null;
+  }
+
+  video.value.play();
+  isVideoPause.value = true;
+
+  setTimeout(() => {
+    videoLoading.value = false;
+  }, 1000);
+}
+
+// 關閉主播視窗 (結束錄音)
+function closeRec() {
+  video.value.pause();
+  isVideoPause.value = true;
+  recordTime.value = 0;
+  clearInterval(timer); // 清空計時秒數
+  if (isRecording.value) {
+    recStop();
+  }
+  showAnchor.value = false;
+}
+
+let isVideoPause = ref(true);
+
+// AI 主播影片播放 & 暫停
+function togglePause(val) {
+  if (val === "pause") {
+    // video.value.pause();
+    isVideoPause.value = false;
+
+    if (video.value) {
+      video.value.pause();
+    }
+    if (currentAudio.value) {
+      currentAudio.value.pause(); // 暫停音訊
+    }
+  } else {
+    isVideoPause.value = true;
+
+    if (video.value) {
+      video.value.play();
+    }
+    if (currentAudio.value) {
+      currentAudio.value.play(); // 播放音訊
+      currentAudio.value.addEventListener("ended", onAudioEnded);
+    }
+  }
+}
+
+// 語音暫停
+function handleVoice(state) {
+  if (currentAudio.value) {
+    if (state === "pause") {
+      currentAudio.value.pause();
+      isVideoPause.value = false;
+    } else {
+      currentAudio.value.play();
+      isVideoPause.value = true;
+    }
+  }
+}
+</script>
+
+<template>
+  <div v-if="isLanguagePage" class="lang-content">
+    <button
+      v-for="(item, index) in langList"
+      :key="index"
+      @click="chooseLang(item.value)"
+      class="main-btn"
+    >
+      {{ item.lang }}
+    </button>
+
+    <!-- <div class="ar-test">
+      <router-link to="/ar-tour">AR 導覽測試</router-link>
+    </div> -->
+  </div>
+  <div v-else class="main-containar">
+    <div class="video-content">
+      <video ref="video" preload playsinline>
+        <source :src="videoSrc" type="video/mp4" />
+        <!-- <source src="../assets/video/start_1.mp4" type="video/mp4" /> -->
+        Your browser does not support the video tag.
+      </video>
+
+      <div v-if="videoLoading" class="video-progress">
+        <v-progress-circular
+          color="primary"
+          indeterminate
+        ></v-progress-circular>
+      </div>
+
+      <!-- <img src="../assets/img/logo.png" alt="" /> -->
+      <!-- <video
+        ref="video"
+        preload
+        playsinline
+        :style="{ opacity: !isTTSVideo ? '1' : '0' }"
+      >
+        <source src="../assets/video/start.mp4" type="video/mp4" />
+        Your browser does not support the video tag.
+      </video>
+
+      <video
+        ref="ttsVideo"
+        preload
+        playsinline
+        :style="{ opacity: isTTSVideo ? '1' : '0' }"
+      >
+        <source :src="ttsVideoSrc" type="video/mp4" />
+
+        Your browser does not support the video tag.
+      </video> -->
+
+      <!-- <button @click="togglePause('pause')" class="control-btn">
+        <img v-if="!isVideoPause" src="../assets/img/pause-button.png" alt="" />
+        <img v-else src="../assets/img/play-button.png" alt="" />
+      </button> -->
+
+      <button
+        v-if="isVideoPause"
+        @click="togglePause('pause')"
+        class="control-btn"
+      >
+        <img src="../assets/img/pause-button.png" alt="" />
+        <!-- <p>語音</p> -->
+      </button>
+
+      <button v-else @click="togglePause('play')" class="control-btn">
+        <img src="../assets/img/play-button.png" alt="" />
+        <!-- <p>語音</p> -->
+      </button>
+
+      <div class="control-item">
+        <!-- <p class="text-center">連接中…</p> -->
+
+        <div class="d-flex flex-column align-center">
+          <audio v-if="audioURL" :src="audioURL" controls></audio>
+          <!-- <p
+          v-if="!isRecording"
+          class="mb-3 text-center"
+          v-html="t('system_construction')"
+        ></p> -->
+          <p v-if="!isRecording" class="mb-3">
+            {{ t("tap_to_record") }}
+          </p>
+          <p v-else class="mb-3">錄音中:{{ recordTime }} 秒</p>
+          <!-- 錄音按鈕 -->
+          <v-btn
+            v-if="!isRecording"
+            @click="recStart"
+            icon="mdi-circle"
+            size="large"
+          >
+            <v-icon icon="mdi-circle" color="red" size="large"></v-icon>
+          </v-btn>
+
+          <v-btn
+            v-else
+            @click="recStop"
+            icon="mdi-circle"
+            size="large"
+            color="success"
+          >
+            <v-icon icon="mdi-square" size="large"></v-icon>
+          </v-btn>
+        </div>
+
+        <div class="d-flex justify-space-between mt-3 mx-10">
+          <!-- 暫停按鈕 -->
+          <!-- <v-btn
+          v-if="isVideoPause"
+          @click="togglePause('pause')"
+          icon="mdi-pause"
+          size="large"
+          color="grey-darken-3"
+        ></v-btn> -->
+
+          <!-- <v-btn
+          v-else
+          @click="togglePause('play')"
+          icon="mdi-play"
+          size="large"
+          color="grey-darken-3"
+        ></v-btn> -->
+
+          <!-- 關閉按鈕 -->
+          <!-- <v-btn
+          @click="closeRec()"
+          icon="mdi-close-thick"
+          size="large"
+          color="red"
+        ></v-btn> -->
+        </div>
+      </div>
+
+      <!-- <ArGuided /> -->
+
+      <!-- <div class="dialog-content pa-4">
+        <v-select
+          v-model="selectArGuided"
+          @update:modelValue="handleSelectChange"
+          label="AR 導覽"
+          :items="['5號 b1 松智藍梯梯廳 > 五樓觀景台售票處']"
+          variant="solo"
+          density="compact"
+          hide-details
+          style="min-width: 120px"
+        ></v-select>
+
+        <v-dialog v-model="arGuidedDialog" width="auto">
+          <v-card width="85vw" height="1000" class="rounded-lg">
+            <ArGuided />
+
+            <v-btn
+              class="mt-auto ms-auto mr-5 mb-5"
+              text="結束導覽"
+              @click="arGuidedDialog = false"
+            ></v-btn>
+          </v-card>
+        </v-dialog>
+      </div> -->
+    </div>
+
+    <div class="chat-content">
+      <div class="headline">
+        <h1>{{ t("title") }}</h1>
+        <!-- <button @click="isRotate = !isRotate">
+          <img
+            :class="{ rotate: isRotate }"
+            src="../assets/img/angles-up-solid.svg"
+            alt=""
+          />
+        </button> -->
+      </div>
+      <section
+        ref="chatArea"
+        class="chat-area"
+        :class="{ 'area-open': isRotate, 'hide-menu': hideMenu }"
+        :style="{ paddingBottom: !hideMenu ? menuHeight + 20 + 'px' : '70px' }"
+      >
+        <div v-for="message in messages" class="message-content">
+          <p
+            v-if="message.label === 'text'"
+            class="message animate__animated"
+            :class="{
+              'message-out': message.author === 'user',
+              'message-in': message.author !== 'user',
+              animate__fadeInRight: message.author === 'user',
+              animate__fadeInLeft: message.author !== 'user',
+            }"
+            v-html="message.body"
+          ></p>
+
+          <!-- 問題類別 -->
+          <!-- <div v-else-if="message.label === 'btn-list'">
+            <ul class="btn-list">
+              <li v-for="(btn, index) in btnList">
+                <button
+                  :class="{ active: index === assignCategoryIndex }"
+                  @click="selectCategory(btn.value, index)"
+                >
+                  {{ t(`${btn.title}`) }}
+                </button>
+              </li>
+            </ul>
+          </div> -->
+
+          <!-- 秘境花園觀景台 -->
+          <div v-else-if="message.label === 'observation_deck'">
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button @click="handleObservationDialog(item.value)">
+                    {{ t(item.text) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 確認按鈕 -->
+          <div v-else-if="message.label === 'check'">
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button @click="handleObservationDialog(item.value)">
+                    {{ t(item.text) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 參觀資訊 -->
+          <div v-else-if="message.label === 'visit'">
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button
+                    @click="handleVisit(t(item.title), t(item.depiction))"
+                  >
+                    {{ t(item.title) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 位置導引 (定位點) -->
+          <div v-else-if="message.label === 'location'">
+            <!-- <v-select
+              @update:modelValue="changeLocation"
+              v-model="assignLocation"
+              label="當前位置"
+              :items="locationList"
+              item-title="location"
+              item-value="location"
+              variant="outlined"
+              density="comfortable"
+              hide-details
+              color="primary"
+              class="mt-5 me-15"
+              :menu-props="{ maxHeight: '155px' }"
+            ></v-select> -->
+
+            <!-- 掃描 QR Code -->
+            <v-btn @click="qrCodeDialog = true" color="primary" class="mt-5">
+              {{ t("tap_to_start_scan") }}
+            </v-btn>
+
+            <v-dialog v-model="qrCodeDialog" width="auto">
+              <v-card max-width="400">
+                <v-card-text>
+                  <div v-if="qrCodeLoading" class="d-flex justify-center pt-5">
+                    <v-progress-circular
+                      color="primary"
+                      indeterminate
+                    ></v-progress-circular>
+                  </div>
+
+                  <qrcode-stream
+                    v-show="!qrCodeLoading"
+                    @detect="onDetect"
+                    @camera-on="onInit"
+                  ></qrcode-stream>
+                </v-card-text>
+
+                <template v-slot:actions>
+                  <v-btn class="ms-auto" @click="qrCodeDialog = false">
+                    {{ t("close") }}
+                  </v-btn>
+                </template>
+              </v-card>
+            </v-dialog>
+
+            <!-- <div class="d-flex">
+              <v-menu location="top">
+                <template v-slot:activator="{ props }">
+                  <v-btn class="mt-5 py-2" color="primary" dark v-bind="props">
+                    <p class="me-2">{{ assignLocation }}</p>
+                    <svg
+                      xmlns="http://www.w3.org/2000/svg"
+                      width="18"
+                      height="18"
+                      fill="currentColor"
+                      class="bi bi-caret-down-fill pt-1"
+                      viewBox="0 0 16 16"
+                    >
+                      <path
+                        d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"
+                      />
+                    </svg>
+                  </v-btn>
+                </template>
+
+                <v-list style="max-height: 155px; overflow-y: auto">
+                  <v-list-item
+                    v-for="(item, index) in locationList"
+                    :key="index"
+                  >
+                    <v-list-item-title @click="changeLocation(item)">
+                      {{ index + 1 }}. {{ item.location }}
+                    </v-list-item-title>
+                  </v-list-item>
+                </v-list>
+              </v-menu>
+
+              <v-menu location="top">
+                <template v-slot:activator="{ props }">
+                  <v-btn
+                    class="ms-5 mt-5 py-2"
+                    color="primary"
+                    dark
+                    v-bind="props"
+                  >
+                    <p class="me-2">查看平面圖</p>
+                    <svg
+                      xmlns="http://www.w3.org/2000/svg"
+                      width="18"
+                      height="18"
+                      fill="currentColor"
+                      class="bi bi-caret-down-fill pt-1"
+                      viewBox="0 0 16 16"
+                    >
+                      <path
+                        d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"
+                      />
+                    </svg>
+                  </v-btn>
+                </template>
+
+                <v-list style="max-height: 155px; overflow-y: auto">
+                  <v-list-item v-for="(item, index) in mapList" :key="index">
+                    <v-list-item-title @click="assignMapImg(item)">
+                      {{ item.title }}
+                    </v-list-item-title>
+                  </v-list-item>
+                </v-list>
+              </v-menu>
+            </div> -->
+          </div>
+
+          <!-- 定位點平面圖 -->
+          <!-- <div v-else-if="message.label === 'map_img'" class="mt-5">
+            <img
+              class="map-img"
+              :class="{ 'show-anchor': showAnchor }"
+              :src="`../src/assets/img/map/${message.body}.webp`"
+              alt=""
+            />
+          </div> -->
+
+          <!-- 位置導引 (導覽點) -->
+          <div v-else-if="message.label === 'navigation'">
+            <!-- <v-select
+              @update:modelValue="changeLocation"
+              v-model="assignNavigation"
+              label="導覽位置"
+              :items="assignNavigationList"
+              variant="solo"
+              hide-details
+              class="bg-white mt-4"
+            ></v-select> -->
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button
+                    @click="
+                      getArviews(item.value, t(`navigation.${item.text}`))
+                    "
+                  >
+                    {{ t(`navigation.${item.text}`) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 秘境花園觀景台 -->
+          <div v-else-if="message.label === 'garden_route'">
+            <!-- <v-select
+              @update:modelValue="changeLocation"
+              v-model="assignNavigation"
+              label="導覽位置"
+              :items="assignNavigationList"
+              variant="solo"
+              hide-details
+              class="bg-white mt-4"
+            ></v-select> -->
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button
+                    @click="getArviews(item.value, t(item.text), 'garden')"
+                  >
+                    {{ t(item.text) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- AR 導覽影片 -->
+          <div v-else-if="message.label === 'ar_views'">
+            <div
+              class="message animate__animated"
+              :class="{
+                'message-out': message.author === 'user',
+                'message-in': message.author !== 'user',
+                animate__fadeInRight: message.author === 'user',
+                animate__fadeInLeft: message.author !== 'user',
+              }"
+            >
+              <p v-html="message.body.words"></p>
+
+              <button @click="arVideoDialog = true" class="ar-link my-3">
+                <!-- 進行 AR 導覽 -->
+                {{ t("ar_tour") }}
+              </button>
+
+              <v-dialog v-model="arVideoDialog" width="auto">
+                <v-card class="ar-card">
+                  <v-card-title class="mt-3"> AR 導覽 </v-card-title>
+
+                  <v-card-text class="pa-0">
+                    <a-scene>
+                      <a-assets>
+                        <video
+                          id="vr-video"
+                          ref="arVideo"
+                          :src="message.body.url"
+                          autoplay
+                          crossorigin="anonymous"
+                          ecrossorigin="anonymous"
+                        ></video>
+                      </a-assets>
+
+                      <a-sky src="#vr-video"></a-sky>
+                    </a-scene>
+                  </v-card-text>
+
+                  <template v-slot:actions>
+                    <v-btn class="ms-auto" @click="arVideoDialog = false">
+                      {{ t("close") }}
+                    </v-btn>
+                  </template>
+                </v-card>
+              </v-dialog>
+            </div>
+          </div>
+
+          <!-- 美食伴手禮 -->
+          <div v-else-if="message.label === 'dining'">
+            <!-- <ul class="btn-options">
+              <li v-for="item in message.body">
+                <button @click="findBrand(item.value)">
+                  {{ t(item.text) }}
+                </button>
+              </li>
+            </ul> -->
+
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button @click="findBrand(item.value)">
+                    {{ t(item.text) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 購物及優惠 -->
+          <div v-else-if="message.label === 'shopping'">
+            <!-- <ul class="btn-options">
+              <li v-for="item in message.body">
+                <button @click="handleShoppingDialog(item.value)">
+                  {{ t(item.text) }}
+                </button>
+              </li>
+            </ul> -->
+
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button @click="handleShoppingDialog(item.value)">
+                    {{ t(item.text) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 購物品牌查詢 -->
+          <div v-else-if="message.label === 'shopping_brand'">
+            <!-- <ul class="btn-options">
+              <li v-for="item in message.body">
+                <button @click="findBrand(item.value)">
+                  {{ item.value }}
+                </button>
+              </li>
+            </ul> -->
+
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button @click="findBrand(item.value)">
+                    {{ t(item.text) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 服務資訊 (常見問題) -->
+          <div v-else-if="message.label === 'service'">
+            <swiper
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="btn-container">
+                  <button
+                    @click="handleService(t(item.title), t(item.depiction))"
+                  >
+                    {{ t(item.title) }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 推薦資訊 -->
+          <div v-else-if="message.label === 'ticket'" class="ticket-item">
+            <!-- 按鈕 -->
+            <swiper
+              v-if="message.body.buttonList.length"
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+              :style="{
+                marginBottom: message.body.ticketList.length ? '35px' : '0px',
+              }"
+            >
+              <swiper-slide v-for="item in message.body.buttonList">
+                <div class="btn-container">
+                  <button @click="setBtnValue(item.info.value)">
+                    {{ item.info.key }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+
+            <!-- 票券 -->
+            <swiper
+              v-if="message.body.ticketList.length"
+              :slidesPerView="1"
+              :navigation="true"
+              :modules="modules"
+              class="ticket-slide"
+            >
+              <swiper-slide v-for="item in message.body.ticketList">
+                <div class="slide-item">
+                  <img
+                    class="cover-img"
+                    :src="item.info.img || item.info.cover_img"
+                    alt=""
+                  />
+                  <section>
+                    <h3 class="title">
+                      {{ item.info.name || item.info.title }}
+                    </h3>
+                    <div class="price-info">
+                      <span>{{ item.info.floor || item.info.price }}</span>
+                      <div class="link-btn">
+                        <a
+                          :href="item.info.website_url || item.info.url"
+                          target="_blank"
+                        >
+                          {{ t("ctaGoUrl") }}
+                        </a>
+                      </div>
+                    </div>
+                    <p class="description">
+                      {{ item.info.content || item.info.description }}
+                    </p>
+                  </section>
+                </div>
+              </swiper-slide>
+            </swiper>
+          </div>
+
+          <!-- 品牌資訊 -->
+          <div v-else-if="message.label === 'brand'" class="ticket-item">
+            <!-- 按鈕 -->
+            <swiper
+              v-if="message.body.buttonList?.length"
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+              class="btn-swiper"
+              :style="{
+                marginBottom: message.body.ticketList.length ? '35px' : '0px',
+              }"
+            >
+              <swiper-slide v-for="item in message.body.buttonList">
+                <div class="btn-container">
+                  <button @click="setBtnValue(item.info.value)">
+                    {{ item.info.key }}
+                  </button>
+                </div>
+              </swiper-slide>
+            </swiper>
+
+            <!-- 票券 -->
+            <swiper
+              v-if="message.body.ticketList?.length"
+              :slidesPerView="1"
+              :navigation="true"
+              :modules="modules"
+              class="ticket-slide"
+            >
+              <swiper-slide v-for="item in message.body.ticketList">
+                <div class="slide-item">
+                  <img
+                    class="cover-img aa"
+                    :src="item.info.img || item.info.cover_img"
+                    alt=""
+                  />
+                  <section>
+                    <h3 class="title">
+                      {{ item.info.name || item.info.title }}
+                    </h3>
+                    <div class="price-info">
+                      <span>{{ item.info.floor || item.info.price }}</span>
+                      <div class="link-btn">
+                        <a :href="item.info.url" target="_blank">
+                          {{ t("ctaGoUrl") }}
+                        </a>
+                      </div>
+                    </div>
+                    <p class="description">
+                      {{ item.info.content || item.info.description }}
+                    </p>
+                  </section>
+                </div>
+              </swiper-slide>
+            </swiper>
+
+            <!-- 美食/伴手禮 -->
+            <swiper
+              v-if="message.body.length"
+              :slidesPerView="1"
+              :navigation="true"
+              :modules="modules"
+              class="ticket-slide"
+            >
+              <swiper-slide v-for="item in message.body">
+                <div class="slide-item">
+                  <img
+                    class="cover-img"
+                    :src="item.info.img || item.info.cover_img"
+                    alt=""
+                  />
+                  <section>
+                    <h3 class="title">
+                      {{ item.info.name || item.info.title }}
+                    </h3>
+
+                    <div
+                      v-if="item.info.phone !== ''"
+                      class="d-flex align-center mt-3 mb-5"
+                    >
+                      <img
+                        src="../assets/img/phone-solid.svg"
+                        width="15"
+                        alt=""
+                      />
+                      <p class="ms-2">
+                        {{ item.info.phone }}
+                      </p>
+                    </div>
+
+                    <div class="price-info">
+                      <span>{{ item.info.floor || item.info.price }}</span>
+                      <div class="link-btn">
+                        <a :href="item.info.url" target="_blank">
+                          {{ t("ctaGoUrl") }}
+                        </a>
+                      </div>
+                    </div>
+
+                    <p class="description">
+                      {{ item.info.content || item.info.description }}
+                    </p>
+                  </section>
+                </div>
+              </swiper-slide>
+            </swiper>
+
+            <!-- <swiper
+              v-if="message.body.length"
+              :slidesPerView="'auto'"
+              :spaceBetween="20"
+              :modules="modules"
+            >
+              <swiper-slide v-for="item in message.body">
+                <img
+                  class="cover-img"
+                  :src="item.info.img || item.info.cover_img"
+                  alt=""
+                />
+                <section>
+                  <h3 class="title">{{ item.info.name || item.info.title }}</h3>
+
+                  <div
+                    v-if="item.info.phone !== ''"
+                    class="d-flex align-center mt-3 mb-5"
+                  >
+                    <img
+                      src="../assets/img/phone-solid.svg"
+                      width="15"
+                      alt=""
+                    />
+                    <p class="ms-2">
+                      {{ getPhoneNumber(item.info.phone) }}
+                    </p>
+                  </div>
+
+                  <div class="price-info">
+                    <span>{{ item.info.floor || item.info.price }}</span>
+                    <div class="link-btn">
+                      <a :href="item.info.url" target="_blank">
+                        {{ t("ctaGoUrl") }}
+                      </a>
+                    </div>
+                  </div>
+
+                  <p class="description">
+                    {{ item.info.content || item.info.description }}
+                  </p>
+                </section>
+              </swiper-slide>
+            </swiper> -->
+          </div>
+
+          <!-- 推薦資訊 -->
+          <div v-else-if="message.label === 'recommend'" class="recommend-item">
+            <img class="cover-img" :src="message.body.cover_img" alt="" />
+            <section>
+              <h3 class="title">{{ message.body.title }}</h3>
+              <p class="description">{{ message.body.description }}</p>
+              <div class="link-btn">
+                <a
+                  :href="message.body.store_info_url || message.body.url"
+                  target="_blank"
+                >
+                  {{ t("ctaBuyNow") }}
+                </a>
+                <a :href="message.body.website_url" target="_blank"> 官網 </a>
+              </div>
+
+              <p class="date">{{ message.body.date }} 限時搶購</p>
+            </section>
+          </div>
+
+          <!-- 店家資訊 -->
+          <div v-else-if="message.label === 'store'" class="store-item">
+            <p class="headline">店家資訊</p>
+            <div class="cover-img">
+              <img :src="message.body.cover_img" alt="" />
+              <h3 class="title">{{ message.body.title }}</h3>
+            </div>
+            <section>
+              <div class="info">
+                <p>
+                  <img
+                    src="../assets/img/location-dot-solid.svg"
+                    width="15"
+                    alt=""
+                  />
+                  {{ message.body.location }}
+                </p>
+                <p>
+                  <img src="../assets/img/phone-solid.svg" width="15" alt="" />
+                  {{ message.body.contact }}
+                </p>
+              </div>
+              <p class="description">{{ message.body.description }}</p>
+            </section>
+          </div>
+
+          <!-- 套票資訊 -->
+          <div v-else-if="message.label === 'ticket'" class="ticket-item">
+            <img class="cover-img" :src="message.body.cover_img" alt="" />
+            <section>
+              <h3 class="title">{{ message.body.title }}</h3>
+              <div class="link-btn">
+                <a :href="message.body.store_info_url" target="_blank">
+                  線上預約
+                </a>
+              </div>
+              <p class="description">{{ message.body.description }}</p>
+              <span class="price-item">{{ message.body.price }}</span>
+            </section>
+          </div>
+        </div>
+      </section>
+
+      <!-- <form @submit.prevent="sendMessage('text')" class="chat-inputs">
+        <input
+          v-model="userMessage"
+          type="text"
+          placeholder="Type a message..."
+        />
+        <button type="submit">
+          <img width="20" src="../assets/img/paper-plane-solid.svg" alt="" />
+        </button>
+      </form> -->
+
+      <!-- 底部選單 -->
+      <div ref="menu" class="menu">
+        <!-- AI 主播影片 -->
+        <!-- <div
+        v-show="showAnchor"
+        class="video-content"
+        :class="{ 'video-down': hideMenu }"
+      >
+        <div v-show="!isTTSVideo">
+          <video ref="video" preload playsinline>
+            <source src="../assets/video/start_1.mp4" type="video/mp4" />
+            Your browser does not support the video tag.
+          </video>
+        </div>
+
+        <div v-show="isTTSVideo">
+          <video ref="ttsVideo" preload playsinline>
+            <source
+              :src="`https://cmm.ai:9001/${ttsVideoSrc}`"
+              type="video/mp4"
+            />
+
+            Your browser does not support the video tag.
+          </video>
+        </div>
+      </div> -->
+
+        <div class="menu-table" :class="{ 'hide-table': hideMenu }">
+          <table class="mt-3">
+            <tbody>
+              <tr v-for="(row, rowIndex) in menuList" :key="rowIndex">
+                <td v-for="(item, itemIndex) in row" :key="itemIndex">
+                  <button @click="selectCategory(item.value, itemIndex)">
+                    <img :src="getImageUrl(item.imgSrc)" alt="" class="icon" />
+                    <p>{{ t(`${item.text}`) }}</p>
+                  </button>
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+
+        <!-- <table class="mt-3">
+        <tbody>
+          <tr v-for="(row, rowIndex) in menuList" :key="rowIndex">
+            <td v-for="(item, itemIndex) in row" :key="itemIndex">
+              <button @click="selectCategory(item.value, itemIndex)">
+                <img :src="getImageUrl(item.imgSrc)" alt="" class="icon" />
+                <p>{{ t(`${item.text}`) }}</p>
+              </button>
+            </td>
+          </tr>
+        </tbody>
+      </table> -->
+
+        <div class="d-flex align-center position-relative">
+          <div class="position-absolute">
+            <button
+              v-if="!showInput"
+              @click="
+                hideMenu = true;
+                showInput = true;
+              "
+              class="ms-3 pt-1"
+            >
+              <img src="../assets/img/icon/素材-04.png" alt="" width="35" />
+            </button>
+
+            <button
+              v-else
+              @click="
+                hideMenu = false;
+                showInput = false;
+              "
+            >
+              <img
+                src="../assets/img/icon/素材-02.png"
+                alt=""
+                width="50"
+                class="pt-2"
+              />
+            </button>
+          </div>
+
+          <div class="w-100 d-flex align-center justify-center">
+            <button
+              v-if="!showInput"
+              @click="
+                hideMenu = true;
+                showInput = true;
+              "
+              class="d-flex align-center question-btn"
+            >
+              <img
+                class="me-2"
+                src="../assets/img/icon/素材-03.png"
+                alt=""
+                width="45"
+              />
+              {{ t("question") }}
+            </button>
+
+            <!-- 對話輸入框 -->
+            <form
+              v-else
+              @submit.prevent="sendMessage()"
+              class="chat-inputs"
+              :class="{ 'd-none': !showInput }"
+            >
+              <!-- <button @click="hideMenu = false" class="menu-btn">
+              <img src="../assets/img/icon/素材-02.png" alt="" width="50" />
+            </button> -->
+
+              <input
+                v-model="userMessage"
+                type="text"
+                placeholder="Type a message..."
+              />
+              <button type="submit" class="submit">
+                <img
+                  width="20"
+                  src="../assets/img/paper-plane-solid.svg"
+                  alt=""
+                />
+              </button>
+            </form>
+          </div>
+        </div>
+      </div>
+
+      <!-- 廣告輪播 -->
+      <!-- <div class="slider-content">
+        <swiper
+          :slidesPerView="1"
+          :autoplay="{
+            delay: 5000,
+            disableOnInteraction: false,
+          }"
+          :modules="modules"
+        >
+          <swiper-slide>
+            <img src="../assets/img/banner-1.jpg" alt="" />
+          </swiper-slide>
+          <swiper-slide>
+            <img src="../assets/img/banner-2.jpg" alt="" />
+          </swiper-slide>
+        </swiper>
+      </div> -->
+    </div>
+  </div>
+
+  <!-- 主要廣告 -->
+  <!-- <v-dialog v-model="showAd">
+    <div class="ad-content">
+      <button @click="showAd = false" class="close-btn">
+        <svg
+          xmlns="http://www.w3.org/2000/svg"
+          width="28"
+          height="28"
+          fill="currentColor"
+          class="bi bi-x-circle"
+          viewBox="0 0 16 16"
+        >
+          <path
+            d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"
+          />
+          <path
+            d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708"
+          />
+        </svg>
+      </button>
+
+      <img class="cover-img" :src="ad.cover_img" alt="" />
+      <section>
+        <h2 class="title">{{ ad.title }}</h2>
+        <span></span>
+        <p v-html="ad.description"></p>
+
+        <div v-if="ad.included.length" class="product">
+          <p>訂單內含商品</p>
+          <ul>
+            <li v-for="(product, index) in ad.included" :key="index">
+              {{ product }}
+            </li>
+          </ul>
+        </div>
+
+        <div v-if="ad.branch.length" class="branch">
+          <p>分店資訊</p>
+          <ul>
+            <li v-for="(name, index) in ad.branch" :key="index">
+              {{ name }}
+            </li>
+          </ul>
+        </div>
+
+        <span v-if="ad.location !== ''" class="info justify-end">
+          <svg
+            xmlns="http://www.w3.org/2000/svg"
+            width="20"
+            height="20"
+            viewBox="0 0 384 512"
+          >
+            <path
+              d="M215.7 499.2C267 435 384 279.4 384 192C384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2c12.3 15.3 35.1 15.3 47.4 0zM192 128a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"
+            />
+          </svg>
+          <p>{{ ad.location }}</p>
+        </span>
+      </section>
+    </div>
+  </v-dialog> -->
+</template>
+
+<style lang="scss">
+.main-btn {
+  padding: 16px 70px;
+  font-size: 22px;
+  font-weight: 600;
+  border: none;
+  border-radius: 100px;
+  letter-spacing: 2px;
+  color: white;
+  background-color: var(--main-color);
+  cursor: pointer;
+  transition: all 0.3s;
+  &:hover {
+    background-color: #c19c75;
+  }
+}
+
+.lang-content {
+  display: flex;
+  flex-direction: column;
+  .main-btn {
+    margin-bottom: 40px;
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+}
+
+.main-containar {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  overflow-x: hidden;
+
+  .video-content {
+    height: 73vh;
+    display: flex;
+    justify-content: center;
+    transform: scale(1);
+    // background-color: #cab78e;
+    background-color: var(--sub-color);
+
+    video {
+      width: 100%;
+      height: 100%;
+      position: fixed;
+      top: 55px;
+      left: 0;
+      right: 0;
+      z-index: 10;
+
+      @media (max-width: 575px) {
+        top: 15px;
+      }
+
+      @media (max-width: 375px) {
+        top: 40px;
+      }
+    }
+
+    .control-btn {
+      width: 33px;
+      height: 33px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      position: absolute;
+      z-index: 50;
+      top: 70px;
+      right: 7px;
+      background: var(--main-color);
+      border: none;
+      border-radius: 100px;
+
+      img {
+        width: 25px;
+        filter: invert(100%) sepia(0%) saturate(0%) hue-rotate(93deg)
+          brightness(103%) contrast(103%); // 改成白色
+      }
+    }
+
+    .control-item {
+      display: flex;
+      justify-content: end;
+      flex-direction: column;
+      z-index: 10;
+      position: absolute;
+      bottom: 20px;
+
+      @media (max-width: 375px) {
+        height: 29vh;
+      }
+
+      p {
+        color: #fff;
+        font-size: 0.875rem;
+        letter-spacing: 1px;
+        text-shadow: 1px 1px 2px #333;
+      }
+    }
+  }
+
+  .chat-content {
+    width: 100%;
+    height: 60vh;
+    margin-top: -20px;
+    position: relative;
+    z-index: 100;
+    letter-spacing: 1px;
+    border-radius: 10px 10px 0 0;
+    // box-shadow: 2px 2px 8px #ccc;
+
+    .headline {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 10px 25px;
+      border-radius: 15px 15px 0 0;
+      background-color: var(--main-color);
+
+      h1 {
+        font-size: 1.125rem;
+        font-weight: 500;
+        color: white;
+
+        @media (max-width: 375px) {
+          font-size: 0.875rem;
+        }
+      }
+
+      button {
+        padding-top: 3px;
+        display: flex;
+        align-items: center;
+        border: none;
+        background-color: transparent;
+        cursor: pointer;
+
+        img {
+          width: 25px;
+          height: 20px;
+          transition: all 0.3s;
+          transform: rotate(0deg);
+
+          &.rotate {
+            transform: rotate(180deg);
+          }
+        }
+      }
+    }
+
+    .chat-area {
+      display: flex;
+      flex-direction: column;
+      background: var(--sub-color);
+      height: 40vh;
+      padding: 0 1em 2em;
+      overflow-x: hidden;
+      overflow-y: auto;
+      transition: all 0.3s;
+
+      // @media (max-width: 575px) {
+      //   height: 50vh;
+      // }
+
+      &.area-open {
+        height: 75vh;
+
+        @media (max-width: 400px) {
+          height: 67vh;
+        }
+      }
+
+      .btn-list {
+        margin-top: 20px;
+        display: flex;
+        flex-wrap: wrap;
+        list-style: none;
+
+        button {
+          margin-right: 15px;
+          margin-bottom: 15px;
+          padding: 5px 15px;
+          color: white;
+          border: 2px solid white;
+          letter-spacing: 2px;
+          font-weight: bold;
+          text-shadow: 1px 1px 1px #939393;
+
+          &.active {
+            color: var(--sub-color);
+            background-color: white;
+            text-shadow: none;
+          }
+        }
+      }
+    }
+
+    .message-content {
+      display: flex;
+      flex-direction: column;
+      // margin-top: 20px;
+
+      .message {
+        max-width: 45%;
+        border-radius: 20px;
+        padding: 0.5em 1.2em;
+        white-space: pre-line;
+
+        &:first-child {
+          margin-top: 0;
+        }
+
+        @media (max-width: 600px) {
+          max-width: 80%;
+          font-size: 0.875rem;
+        }
+
+        &.message-out {
+          margin-left: auto;
+          background: var(--bg-grey);
+          color: white;
+        }
+
+        &.message-in {
+          margin-right: auto;
+          background: #f1f0f0;
+          color: black;
+        }
+
+        &.message-in,
+        &.message-out {
+          margin-top: 20px;
+        }
+      }
+    }
+
+    .branch {
+      ul,
+      li,
+      p {
+        margin: 10px 0;
+        font-size: 14px;
+      }
+    }
+
+    .chat-inputs {
+      width: 100%;
+      display: flex;
+      padding: 13px 20px;
+      background-color: white;
+
+      input {
+        width: 100%;
+        border: none;
+        margin-left: 2rem;
+
+        &:focus-visible {
+          outline: none;
+        }
+      }
+
+      button {
+        border: none;
+        background: white;
+        cursor: pointer;
+        &:hover {
+          img {
+            opacity: 0.8;
+          }
+        }
+
+        img {
+          padding-top: 5px;
+          transition: all 0.3s;
+          filter: invert(75%) sepia(20%) saturate(459%) hue-rotate(360deg)
+            brightness(100%) contrast(85%);
+        }
+      }
+
+      ::placeholder {
+        font-size: 1rem;
+        font-weight: 500;
+        color: var(--sub-color);
+        opacity: 1; /* Firefox */
+      }
+
+      ::-ms-input-placeholder {
+        /* Edge 12 -18 */
+        color: var(--sub-color);
+      }
+    }
+
+    .recommend-item {
+      width: 350px;
+      border-radius: 10px;
+      background-color: white;
+    }
+
+    .recommend-item,
+    .ticket-item {
+      .cover-img {
+        width: 100%;
+        height: 22.5vh;
+        object-fit: contain;
+        overflow: hidden;
+        border-radius: 10px 10px 0 0;
+      }
+
+      section {
+        padding: 15px 25px;
+        letter-spacing: 1px;
+
+        .title {
+          margin-bottom: 0;
+          font-weight: 600;
+          font-size: 1.25rem;
+          text-align: left;
+          // 超過則省略
+          overflow: hidden;
+          text-overflow: ellipsis;
+          display: -webkit-box;
+          -webkit-line-clamp: 1;
+          -webkit-box-orient: vertical;
+          line-break: after-white-space;
+        }
+
+        .description {
+          line-height: 1.8;
+          // text-align: justify;
+          font-size: 0.875rem;
+        }
+
+        .date {
+          color: #646464;
+          font-size: 0.875rem;
+          text-align: center;
+        }
+      }
+
+      .link-btn {
+        display: flex;
+        justify-content: center;
+        a {
+          display: block;
+          width: 100%;
+          max-width: 200px;
+          margin: 25px 3px;
+          padding: 8px;
+          text-align: center;
+          border-radius: 20px;
+          color: white;
+          background-color: var(--main-color);
+          text-decoration: none;
+          transition: all 0.3s;
+
+          &:hover {
+            opacity: 0.8;
+          }
+        }
+      }
+    }
+
+    .store-item {
+      width: 350px;
+
+      .cover-img {
+        position: relative;
+        margin-bottom: -5px;
+
+        img {
+          width: 100%;
+          height: 200px;
+          object-fit: cover;
+        }
+
+        .title {
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%, -50%);
+          color: white;
+          font-size: 1.25rem;
+          letter-spacing: 2px;
+          text-shadow: 2px 2px 4px #333;
+        }
+      }
+
+      .headline {
+        color: white;
+        justify-content: center;
+      }
+
+      section {
+        padding: 20px;
+        letter-spacing: 1px;
+        border-radius: 0 0 10px 10px;
+        background-color: white;
+
+        .info {
+          margin-bottom: 15px;
+          p {
+            display: flex;
+            align-items: center;
+            img {
+              margin-right: 10px;
+            }
+
+            &:last-child {
+              margin-top: 3px;
+            }
+          }
+        }
+      }
+    }
+
+    .ticket-item {
+      width: 100%;
+      margin-top: 20px;
+
+      h3 {
+        margin-bottom: 10px;
+        text-align: center;
+      }
+
+      .ticket-slide {
+        width: 63%;
+        margin: 0;
+        position: relative;
+
+        @media (max-width: 767px) {
+          width: 90%;
+        }
+
+        .swiper-slide {
+          margin-left: 1px;
+
+          .slide-item {
+            max-width: 50vw;
+            border-radius: 10px;
+            background-color: white;
+
+            @media (max-width: 767px) {
+              max-width: 70vw;
+            }
+          }
+        }
+
+        .swiper-button-prev {
+          display: none;
+        }
+      }
+
+      .title,
+      .description {
+        // 超過則省略
+        overflow: hidden;
+        text-overflow: ellipsis;
+        display: -webkit-box;
+        // -webkit-line-clamp: 3;
+        -webkit-box-orient: vertical;
+        line-break: after-white-space;
+      }
+
+      .title {
+        -webkit-line-clamp: 3;
+      }
+
+      .description {
+        -webkit-line-clamp: 5;
+      }
+
+      section {
+        margin-top: -6px;
+        border-radius: 0 0 10px 10px;
+        background-color: white;
+
+        .link-btn {
+          a {
+            max-width: 150px;
+            margin: 0;
+            padding: 8px 12px;
+            font-size: 0.75rem;
+          }
+        }
+
+        .price-item {
+          display: block;
+          margin-top: 10px;
+          text-align: end;
+        }
+
+        .price-info {
+          margin: 20px 0;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+
+          @media (max-width: 575px) {
+            font-size: 0.875rem;
+            // flex-direction: column;
+            // align-items: start;
+          }
+
+          span {
+            display: block;
+            margin: 10px 0;
+            padding-right: 5px;
+            font-size: 1rem;
+            font-weight: 600;
+            color: var(--main-color);
+          }
+        }
+      }
+    }
+  }
+}
+
+.btn-swiper {
+  .swiper-slide {
+    width: auto;
+    // max-width: 185px;
+
+    .btn-container {
+      display: flex;
+      justify-content: center;
+
+      button {
+        display: block;
+        width: 100%;
+        padding: 10px 20px;
+        border-radius: 10px;
+        color: white;
+        border: none;
+        background-color: var(--main-color);
+        letter-spacing: 2px;
+        font-size: 0.875rem;
+        transition: all 0.3s;
+
+        &:hover {
+          opacity: 0.8;
+        }
+      }
+    }
+  }
+}
+
+.a-modal {
+  display: none !important;
+}
+
+.ad-content {
+  max-height: 95vh;
+  padding: 35px;
+  overflow-y: auto;
+  border-radius: 10px;
+  background-color: #fff;
+
+  .close-btn {
+    position: absolute;
+    top: 7px;
+    right: 5px;
+    border: none;
+    background-color: transparent;
+    transition: all 0.3s;
+    cursor: pointer;
+
+    svg {
+      filter: invert(57%) sepia(45%) saturate(391%) hue-rotate(350deg)
+        brightness(93%) contrast(84%);
+    }
+
+    &:hover {
+      opacity: 0.8;
+    }
+  }
+
+  section {
+    p {
+      padding: 0 10px;
+      line-height: 1.7;
+      letter-spacing: 2px;
+    }
+
+    .title {
+      margin: 15px auto;
+      color: var(--main-color);
+      font-weight: 600;
+      font-size: 1.375em;
+      text-align: center;
+      letter-spacing: 2px;
+    }
+
+    .product,
+    .branch {
+      margin-top: 20px;
+      background-color: #f1e9dc;
+
+      p {
+        padding: 10px 0;
+        font-weight: 500;
+        font-size: 0.875em;
+        text-align: center;
+        border-bottom: 2px solid #fff;
+      }
+
+      ul {
+        padding: 10px;
+        list-style: none;
+
+        li {
+          padding-bottom: 5px;
+          letter-spacing: 1px;
+          font-weight: 500;
+          font-size: 0.875em;
+        }
+      }
+    }
+
+    .date {
+      margin: 20px 0;
+      text-align: center;
+    }
+
+    .map {
+      display: flex;
+      align-items: center;
+      justify-content: end;
+
+      p {
+        padding-left: 10px;
+        font-weight: 500;
+        letter-spacing: 2px;
+        color: var(--main-color);
+      }
+
+      svg {
+        filter: invert(57%) sepia(45%) saturate(391%) hue-rotate(350deg)
+          brightness(93%) contrast(84%);
+      }
+    }
+  }
+
+  .cover-img {
+    width: 100%;
+    object-fit: cover;
+  }
+}
+
+.slider-content {
+  width: 100%;
+
+  img {
+    width: 100%;
+    margin-bottom: -5px;
+  }
+}
+
+.branch {
+  margin-top: 30px;
+  ul {
+    padding: 0;
+    list-style: none;
+
+    li {
+      letter-spacing: 1px;
+    }
+  }
+}
+
+.video-progress {
+  position: absolute;
+  left: 50%;
+  top: 40%;
+  z-index: 100;
+  transform: translate(-50%, -50%);
+}
+
+.dialog-content {
+  position: absolute;
+  top: 60px;
+  left: 0;
+  z-index: 100;
+}
+
+.vr-content {
+  width: 700px;
+  height: 700px;
+}
+
+.ar-link {
+  display: inline-block;
+  padding: 7px 17px;
+  color: #fff;
+  background-color: var(--main-color);
+  border-radius: 5px;
+  text-decoration: none;
+}
+
+.ar-card {
+  width: 85vw;
+  height: 100vh;
+}
+
+.btn-swiper {
+  margin-top: 20px;
+}
+
+.v-expansion-panel-text {
+  font-size: 0.875rem;
+  line-height: 1.7;
+}
+
+// 底部選單
+.menu {
+  position: fixed;
+  z-index: 300;
+  left: 0;
+  bottom: 0px;
+  right: 0;
+  color: var(--text-color);
+  background-color: white;
+  box-shadow: 0px -3px 10px rgba(150, 150, 150, 0.7);
+
+  &.hide-menu table {
+    height: 0;
+    z-index: -1;
+  }
+
+  .icon {
+    width: 80px;
+
+    @media (max-width: 767px) {
+      width: 50px;
+    }
+
+    // @media (max-width: 375px) {
+    //   width: 45px;
+    // }
+  }
+
+  .menu-table {
+    height: 100%;
+    transition: all 0.3s;
+
+    &.hide-table {
+      height: 0;
+
+      table {
+        display: none;
+        height: 0;
+      }
+    }
+
+    table {
+      width: 95%;
+      height: 100%;
+      margin: auto;
+      border-collapse: collapse;
+      position: relative;
+      transition: all 0.3s;
+
+      &::before,
+      &::after {
+        content: "";
+        display: inline-block;
+        width: 10px;
+        height: 100%;
+        background-color: #fff;
+        position: absolute;
+        bottom: 0%;
+
+        @media (max-width: 390px) {
+          width: 8px;
+        }
+      }
+
+      &::before {
+        left: -5px;
+      }
+
+      &::after {
+        right: -5px;
+      }
+
+      tr {
+        td {
+          width: 50px;
+          padding: 1rem 0;
+          text-align: center;
+          border: 1px solid #ccc;
+
+          @media (max-width: 767px) {
+            width: 100px;
+            padding: 0.5rem 0;
+          }
+
+          p {
+            padding: 0 0.3rem;
+            letter-spacing: 0.03rem;
+
+            @media (max-width: 430px) {
+              // width: 115px;
+              font-size: 0.875rem;
+            }
+          }
+        }
+
+        &:first-child {
+          td {
+            border-top: 0px solid transparent;
+          }
+        }
+
+        &:last-child {
+          td {
+            border-bottom: 0px solid transparent;
+          }
+        }
+      }
+    }
+  }
+
+  .question-btn {
+    color: var(--main-color);
+    font-size: 1.25rem;
+    font-weight: 500;
+    letter-spacing: 0.1rem;
+
+    @media (max-width: 414px) {
+      font-size: 1rem;
+    }
+  }
+}
+</style>

+ 27 - 0
src/components/Navbar.vue

@@ -0,0 +1,27 @@
+<script setup>
+import { ref, reactive } from "vue";
+</script>
+
+<template>
+  <div class="navbar">
+    <img src="../assets/img/logo.svg" alt="" class="logo" />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.navbar {
+  display: flex;
+  position: fixed;
+  top: 0;
+  right: 0;
+  left: 0;
+  z-index: 50;
+  padding: 10px 10px 5px;
+  background-color: #fafcf7;
+
+  .logo {
+    margin-left: auto;
+    max-width: 100px;
+  }
+}
+</style>

+ 128 - 0
src/language/en.json

@@ -0,0 +1,128 @@
+{
+  "title": "Artificial intelligence customer service",
+  "prologue": "Hello, welcome to use the Taipei 101 Intelligent Customer Service System.",
+  "service": "How may I help you?",
+  "ctaBuyNow": "Buy now",
+  "ctaGoUrl": "Go now",
+  "customer_show": "AI Customer Service",
+  "customer_hide": "AI Customer Service",
+  "observation_deck": "Observatory",
+  "location_guide": "Location Guide",
+  "food_souvenirs": "Food & Souvenirs",
+  "shopping_discounts": "Shopping and Discounts",
+  "service_information": "Service Information",
+  "ar_tour": "AR navigation",
+  "select_location": "Please select you location for AR navigation",
+  "question": "Click me to ask a question",
+  "qr_code_scan_prompt": "Please scan QR Code to obtain current location",
+  "tap_to_start_scan": "Tap to start scanning",
+  "current_location": "Current location",
+  "select_navigation_location": "Please select navigation location",
+  "system_construction": "The system is under construction. <br> The voice input function is disabled.",
+  "tap_to_record": "Tap to record",
+  "close": "Close",
+  "service_info": {
+    "title": "Service information",
+    "inquiry_prompt": "What are you searching for?<br>Or please enter your question",
+    "business_hours": "Business Hours",
+    "business_hours_1": "Shopping mall<br>Sunday to Thursday 11:00-21:30<br>Friday, Saturday and the day before National Holidays 11:00-22:00<br><br>Observatory<br>Monday to Sunday and National Holidays<br>10:00-21:00",
+    "tax_refund": "Tax Refund",
+    "tax_refund_1": "★Please present today's receipts totaling over NT$2,000 for eligible tax refund items purchased within the Taipei 101 and foreign passport (with entry permit) at the Tax Refund Center on B1.<br><br>▲ Passport with entry stamp must be presented [Mainland China, Hong Kong, Macau visitors - entry permit only]<br>▲ Approximately 3.8% of the tax will be refunded after deducting the handling fee",
+    "toilets": "Toilets / Family Toilets",
+    "toilets_1": "The restroom locations are as follows:<br>B1: Xinyi Circle, Zhong Circle, Songzhi Circle<br>1st to 3rd Floor: Zhong Circle<br>4th Floor: Songzhi Circle (temporarily closed for maintenance)<br>5th Floor: Songzhi Circle (near the observatory ticket office)<br><br>The Taipei 101 Shopping Mall's family toilet is located within the men's and women's toilets on B1 in Zhong Circle, Songzhi Circle, and Xinyi Circle, as well as next to the men's and women's toilets on the 3rd floor in Zhong Circle. In addition, the independent family toilet is located on the 3rd floor in Zhong Circle with regular restroom facilities, also features a diaper changing station, child safety seat, and child-friendly toilet. The spacious layout and enhanced facilities make it more convenient for parents and children to use!",
+    "charge": "Charging",
+    "charge_1": "Please proceed to rent a portable charger next to the Customer Service Center on B1 in the Central Circle."
+  },
+  "food_souvenirs_info": {
+    "searching": "What kind of dining option are you searching for?",
+    "signature": "Signature Restaurants / Sky Dining",
+    "light": "Light Food / Cafes",
+    "courts": "Food Courts",
+    "souvenir": "Souvenir"
+  },
+  "observatory_info": {
+    "tickets": {
+      "title": "Purchase Tickets Online",
+      "purchase": "Would you like to purchase tickets online?",
+      "id_card": "May I ask if you hold a Taiwan ID card?",
+      "yes": "Yes",
+      "no": "No",
+      "ticket_type": "Which type of ticket would you like to purchase?"
+    },
+    "visitor": {
+      "title": "Visitor Information",
+      "question": "Please enter your question in the text box below",
+      "faq": "FAQ",
+      "events_q": "Special Events",
+      "events_a": "The Taipei 101 Observatory is the tallest observation deck in Taiwan, offering a 360-degree panoramic view from 382 meters above ground level. Its height is more than twice than the Elephant Mountain, and it is the only spot where visitors can overlook the Keelung River Valley. Currently, Taipei 101 has created a dreamy photo spot on the 89th floor, featuring \"Secret Garden in the Sky,\" \"Wings of Angels,\" \"Stars and Moon in the Clouds,\" and more. Everyone can immerse themselves in these fairy-tale-like scenes, truly enjoy the moments.",
+      "ball_q": "Wind Damping Ball",
+      "ball_a": "The formal name of the wind damping ball in Taipei 101 is the Tuned Mass Damper (TMD). This passive damping system is customized to meet the specific needs of the building and is located in the central area of floors 87 to 92. Its primary purpose is to reduce the building's sway during strong winds, thereby minimizing discomfort for occupants working in the building. Unlike conventional hidden damping systems, the design of Taipei 101's wind damping ball emphasizes both functionality and aesthetics. Visitors to the observation deck can catch a glimpse of the overall operation of the damping system.",
+      "accessible_q": "Accessible Facilities",
+      "accessible_a": "The observation deck is equipped with accessible restrooms, and if it is inconvenient to use stairs, please ask staff for assistance."
+    },
+    "view": "Realistic Scenery",
+    "garden": "Head to \"Secret Garden in the Clouds\""
+  },
+  "shopping_discounts_info": {
+    "brands": {
+      "title": "Search by brands",
+      "choose": "Please choose the brand category",
+      "jewely": "Jewely & Watches",
+      "boutique": "Fashion Boutique",
+      "skincare": "Cosmetics & Skincare",
+      "apparel": "Fashion Apparel",
+      "lifestyle": "Home and Lifestyle/3C",
+      "cultural": "Cultural and Creative Products",
+      "signature": "Signature Brand",
+      "outside": "Outside Store"
+    },
+    "tourist_card": {
+      "title": "Privileges for Taipei 101 Tourist Card",
+      "content": "Visit Taipei 101 to enjoy 2024 exclusive benefits - Once you apply for the Taipei 101 Tourist Card, you can enjoy exclusive benefits only for international travelers.<br>‧Shopping - Special discounts starting from 10% off on selected brands.<br>‧Gifts - Welcome Pack + NTD300 Cash Voucher<br>‧Tax Refund - Spend over NTD2000 can proceed fast 5% tax refund on B1.<br><br>Welcome to the Customer Service Center on B1 to fill out the application form and enjoy exclusive benefits.<br><br><a href='https://stage.taipei101mall.com.tw/?lang=zh&utm_source=101TTE_PH&utm_medium=Travel_Fair&utm_campaign=Stage' class='ar-link mb-3' target='_blank'>Apply online now</a>",
+      "apply": "Apply online now"
+    }
+  },
+  "location": {
+    "b1_din_tai_fung": "b1 Din Tai Fung",
+    "b1_centre": "b1 Centre",
+    "b1_elevator_no_2": "b1 Elevator No.2",
+    "b1_elevator_no_4": "b1 Elevator No.4",
+    "b1_elevator_no_3": "b1 Elevator No.3",
+    "1f_south_xinyi": "1f South Xinyi",
+    "1f_elevator_no_3": "1f Elevator No.3",
+    "2f_elevator_no_3": "2f Elevator No.3",
+    "88f_observatory": "88f Observatory",
+    "89f_observatory": "89f Observatory"
+  },
+  "navigation": {
+    "observatory_ticket_office": "Observatory Ticket Office",
+    "atm": "ATM",
+    "items_and_luggage_storage": "Items and Luggage Storage",
+    "metro_station": "Metro Station",
+    "taxi_station": "Taxi Station",
+    "101f_queue": "101F Queue",
+    "91f_outdoor_area": "91F Outdoor Area",
+    "ice_warmed_water_dispenser": "Ice-warmed Water Dispenser",
+    "snackable_souvenirs": "Snackable Souvenirs",
+    "high_rise_restaurant_reception": "Reception Desk for High-rise Restaurants",
+    "accessible_facilities": "Accessible Facilities",
+    "tax_refund_currency_exchange": "Tax Refund Services / Currency Exchange",
+    "service_counter": "Service Counter",
+    "service_counter_parking_discount": "Service Counter (Parking Discount)",
+    "restroom": "Restroom",
+    "nursing_room": "Nursing Room",
+    "food_court": "Food Court",
+    "supermarket": "Supermarket",
+    "din_tai_fung": "Din Tai Fung",
+    "all_brands": "All Brands"
+  },
+  "external": {
+    "gifts": "Gifts",
+    "dining": "Dining",
+    "accommodation": "Accommodation",
+    "hairdressing": "Hairdressing",
+    "car_supplies": "Car Supplies",
+    "entertainment": "Entertainment",
+    "pet_supplies": "Pet Supplies"
+  }
+}

+ 128 - 0
src/language/ja.json

@@ -0,0 +1,128 @@
+{
+  "title": "AIインテリジェントカスタマーサービス",
+  "prologue": "こんにちは、台北101インテリジェントカスタマーサービスシステムをご利用いただき、ようこそ。",
+  "service": "どうすればお手伝いできますか?",
+  "ctaBuyNow": "すぐに購入する",
+  "ctaGoUrl": "すぐに行く",
+  "customer_show": "AIインテリジェントカスタマーサービス",
+  "customer_hide": "AIインテリジェントカスタマーサービス",
+  "observation_deck": "秘境の庭園展望台",
+  "location_guide": "位置ガイド",
+  "food_souvenirs": "グルメ/お土産",
+  "shopping_discounts": "ショッピングと特典",
+  "service_information": "サービス情報",
+  "ar_tour": "ARナビを利用",
+  "select_location": "あなたの現在地を選んで、ARナビを利用します。",
+  "question": "質問するにはここをクリック",
+  "qr_code_scan_prompt": "請掃描 QR Code 取得當前位置",
+  "tap_to_start_scan": "點我開啟掃描",
+  "current_location": "當前位置",
+  "select_navigation_location": "請選擇導覽位置",
+  "system_construction": "系統建置中,<br />關閉語音輸入功能。",
+  "tap_to_record": "點選以進行錄音",
+  "close": "關閉",
+  "service_info": {
+    "title": "サービス情報",
+    "inquiry_prompt": "サービス情報を選択<br>下のメッセージボックスにご質問を入力してください。",
+    "business_hours": "営業時間",
+    "business_hours_1": "ショッピングモール<br>日曜日~木曜日11:00-21:30<br>金曜日、土曜日、祝日の前日11:00-22:00<br><br>展望台<br>月曜~日曜・祝日 10:00~21:00",
+    "tax_refund": "免税",
+    "tax_refund_1": "台湾に183日以内に滞在し、購入日から90日以内に出国した外国人旅行者は、同日に台北101ショッピングモールで累計2,000元(税込)のお買い物をされた方は、請求書と入国スタンプが押されたパスポート(香港、マカオ、中国の方は入台証)をお持ちになり、地下1階の「税還付サービスセンター」で還付手続きを行って還付申請書を取得し、書類を持って該当する場所で還付金を受け取ることができます。",
+    "toilets": "トイレ/親子トイレ",
+    "toilets_1": "台北101のショッピングモールには、B1の中央、マツジ、信義サークル内の男性用と女性用のトイレに家族用のトイレがあります。また、2階、3階、4階の男性用と女性用のトイレの隣にもあります。中央サークルの4階には、専用の家族用トイレもあります。通常のトイレ設備に加えて、おむつ交換ステーション、子供用安全座席、子供向けトイレが備わっています。広々としたレイアウトと充実した設備で、親子が利用しやすくなっています!<br><br><button class='ar-link mb-3'>ARナビを利用</button>",
+    "charge": "充電",
+    "charge_1": "地下1階のカスタマサービスセンターにてモバイルバッテリーをレンタルできます。"
+  },
+  "food_souvenirs_info": {
+    "searching": "料理ジャンルを選択",
+    "signature": "空中レストラン",
+    "light": "軽食/CAFÉ",
+    "courts": "レストラン街/フードコート",
+    "souvenir": "お土産"
+  },
+  "observatory_info": {
+    "tickets": {
+      "title": "チケットを購入",
+      "purchase": "チケットをご購入になりますか?",
+      "id_card": "中華民国の身分証明書はお持ちでしょうか?",
+      "yes": "はい",
+      "no": "いいえ",
+      "ticket_type": "どの種類のチケットを購入しますか?"
+    },
+    "visitor": {
+      "title": "観光情報",
+      "question": "下のメッセージボックスにご質問を入力してください。",
+      "faq": "よくある質問:",
+      "events_q": "おすすめのアクティビティ",
+      "events_a": "台北101は台湾で一番高い展望台であり、382メートルの高さで360度全視野で景色を俯瞰するこことができます。その高さは象山の2倍となり、唯一基隆河が俯瞰できるスポットとなっています。現在台北101の89階の展望台に「雲の上のピクニックガーデン」や「天使の翼」や「雲中星月」など、老若問わず楽しめる夢のようなインスタ映えスポット/フォトジェニックポイントがあります。",
+      "ball_q": "ウィンドダンパーエリア",
+      "ball_a": "台北101のウィンドダンパーの正式名称はTMD (Tuned Mass Damper)と言い、建物に合わせて作られたパッシブ制震システムで、87階から92階のフロアの中央に設置されており、強風時の建物の揺れを軽減することによって、建物にいる人たちの不快感を抑えるためです。従来の隠れた制震ダンパーとは異なり、台北 101 のウィンドダンパーは機能と外観両方を兼ねて設計されており、全体的に制震ダンパーの動作を展望台から見ることができます。",
+      "accessible_q": "バリアフリー",
+      "accessible_a": "展望台はバリアフリートイレがございます。階段の上り下りに不自由な方はお近くの係員までお問い合わせください。"
+    },
+    "view": "リアルな景色",
+    "garden": "雲の上のピクニックガーデンへ行く"
+  },
+  "shopping_discounts_info": {
+    "brands": {
+      "title": "カテゴリーを選択",
+      "choose": "ブランド名を選択",
+      "jewely": "インターナショナルジュエリー&ウォッチ",
+      "boutique": "インターナショナルブティックス/ブティック",
+      "skincare": "スキンケア・メイクアップ",
+      "apparel": "ファッションウェア",
+      "lifestyle": "インテリア/家電製品",
+      "cultural": "クリエイティブブランド",
+      "signature": "その他",
+      "outside": "館外の店"
+    },
+    "tourist_card": {
+      "title": "台北101提携カード割引",
+      "content": "2024 年には、台北 101 の提携ブランド カードである Dingji Unlimited Card をスワイプすると、三つの特典を獲得できます。<br>‧ショッピング:ブランドをで 10% 割引<br>‧特典:Welcome Pack+ NTD300現金割引券をプレゼント<br>‧外国人旅行者への税還付:同日に台北101ショッピングモールで累計2,000元(税込)のお買い物をされた方は地下1階の「税還付サービスセンター」ですぐ還付手続きを行えます。<br><br>台北101地下1階カスタマーサービスセンターにて、提携カードを申請し、今すぐ特典をゲットしましょう!<br><br><a href='https://stage.taipei101mall.com.tw/?lang=zh&utm_source=101TTE_PH&utm_medium=Travel_Fair&utm_campaign=Stage' class='ar-link mb-3' target='_blank'>インターネットお申し込み</a>",
+      "apply": "インターネットお申し込み"
+    }
+  },
+  "location": {
+    "b1_din_tai_fung": "B1 鼎泰豐",
+    "b1_centre": "B1 中環",
+    "b1_elevator_no_2": "B1 2號電梯",
+    "b1_elevator_no_4": "B1 4號電梯",
+    "b1_elevator_no_3": "B1 3號電梯",
+    "1f_south_xinyi": "1F 信義環",
+    "1f_elevator_no_3": "1F 3號電梯",
+    "2f_elevator_no_3": "2F 3號電梯",
+    "88f_observatory": "88F 觀景台",
+    "89f_observatory": "89F 觀景台"
+  },
+  "navigation": {
+    "observatory_ticket_office": "觀景台售票處",
+    "atm": "ATM",
+    "items_and_luggage_storage": "置物櫃",
+    "metro_station": "捷運",
+    "taxi_station": "計程車乘車處",
+    "101f_queue": "101F 排隊處",
+    "91f_outdoor_area": "91F 戶外區",
+    "ice_warmed_water_dispenser": "飲水機",
+    "snackable_souvenirs": "伴手禮區",
+    "high_rise_restaurant_reception": "景觀餐廳櫃台",
+    "accessible_facilities": "無障礙廁所",
+    "tax_refund_currency_exchange": "退稅/外幣兌換櫃台",
+    "service_counter": "客服",
+    "service_counter_parking_discount": "客服(停車折抵)",
+    "restroom": "廁所",
+    "nursing_room": "哺乳室",
+    "food_court": "美食街",
+    "supermarket": "超市",
+    "din_tai_fung": "鼎泰豐",
+    "all_brands": "全館品牌"
+  },
+  "external": {
+    "gifts": "伴手禮",
+    "dining": "餐飲",
+    "accommodation": "住宿",
+    "hairdressing": "美髮",
+    "car_supplies": "汽車用品",
+    "entertainment": "休閒娛樂",
+    "pet_supplies": "寵物用品"
+  }
+}

+ 128 - 0
src/language/ko.json

@@ -0,0 +1,128 @@
+{
+  "title": "AI 지능 고객 서비스",
+  "prologue": "안녕하세요. 타이베이 101 지능형 고객 서비스 시스템을 이용해 주셔서 환영합니다.",
+  "service": "어떻게 도와드릴까요?",
+  "ctaBuyNow": "바로 구매",
+  "ctaGoUrl": "즉시 이동",
+  "customer_show": "실제 고객 상담원",
+  "customer_hide": "실제 고객 상담원",
+  "observation_deck": "비경 정원 전망대",
+  "location_guide": "위치 안내",
+  "food_souvenirs": "맛집/기념품",
+  "shopping_discounts": "쇼핑 및 혜택",
+  "service_information": "서비스 정보",
+  "ar_tour": "進行 AR 導覽",
+  "select_location": "請選擇您的位置進行 AR 導覽",
+  "question": "질문하려면 클릭하세요",
+  "qr_code_scan_prompt": "請掃描 QR Code 取得當前位置",
+  "tap_to_start_scan": "點我開啟掃描",
+  "current_location": "當前位置",
+  "select_navigation_location": "請選擇導覽位置",
+  "system_construction": "系統建置中,<br />關閉語音輸入功能。",
+  "tap_to_record": "點選以進行錄音",
+  "close": "關閉",
+  "service_info": {
+    "title": "服務資訊",
+    "inquiry_prompt": "請問您想查詢?",
+    "business_hours": "營業時間",
+    "business_hours_1": "購物中心<br>周日至周四 11:00-21:30<br>周五、周六及國定假日前一天 11:00-22:00<br><br>觀景台<br>周一至周日及國定假日<br>10:00-21:00",
+    "tax_refund": "退稅",
+    "tax_refund_1": "★請持今日於館內累積購買超過新台幣 2,000 元以上可退稅商品之台北101發票及外籍護照(入台證)正本及購買商品至地下一樓退稅中心辦理。",
+    "toilets": "廁所/親子廁所",
+    "toilets_1": "洗手間所在位置於<br>地下一樓:信義環、中環、松智環<br>一樓至三樓:中環<br>四樓:松智環(維修中暫停使用)<br>五樓:松智環(靠近觀景台售票處)<br><br>台北101 購物中心親子廁所,位於地下一樓中環、松智環、信義環之男女廁所內,以及 3 樓中環男女廁旁。3 樓中環之獨立親子廁所,除一般洗手間功能外,另設有尿布檯、兒童安全座椅及兒童專用馬桶,空間寬敞且設施更加完善,方便家長及小朋友使用!",
+    "charge": "充電",
+    "charge_1": "請至B1中環顧客服務中心旁租借行動電源。"
+  },
+  "food_souvenirs_info": {
+    "searching": "請問您想查詢哪一種美食?",
+    "signature": "特色/高空餐廳",
+    "light": "輕食/CAFÉ",
+    "courts": "美食街",
+    "souvenir": "伴手禮"
+  },
+  "observatory_info": {
+    "tickets": {
+      "title": "線上購票",
+      "purchase": "請問您需要線上購票嗎?",
+      "id_card": "請問您是否持有中華民國身分證?",
+      "yes": "有",
+      "no": "沒有",
+      "ticket_type": "請問您要購買哪種門票?"
+    },
+    "visitor": {
+      "title": "參觀資訊",
+      "question": "請於下方文字框輸入您的問題:",
+      "faq": "常見問題提示:",
+      "events_q": "特色活動",
+      "events_a": "台北101觀景台是全台灣最高的觀景台,在382公尺的360度全視野高空俯瞰,高度不僅是象山的兩倍多,更是唯一可以眺望基隆河谷景觀的景點。目前台北101在89F觀景台打造夢幻打卡點,無論是「雲端上的秘境花園」、「天使之翼」、「雲中星月」等,無論是大人還是小孩,都能置身在仙境般的場景,令人陶醉其中。",
+      "ball_q": "風阻尼球",
+      "ball_a": "台北101風阻尼球的完整正式名稱:調諧質量阻尼器TMD (Tuned Mass Damper),是針對本大樓需求量身定做的被動阻尼系統,位置設置於87F~92F的樓層中央位置,主要目的為減低大樓受強風吹襲時之擺動,減少在大樓工作之人員感到不舒適之程度。有別於一般傳統隱藏式阻尼系統,台北101-風阻尼球的設計特別兼顧了功能及外觀,在觀景台將可一窺阻尼系統的整體運作。",
+      "accessible_q": "無障礙設施",
+      "accessible_a": "觀景台設有無障礙廁所,上下樓如果不方便用樓梯行動,也可請服務人員協助。"
+    },
+    "view": "實境景色",
+    "garden": "前往秘境花園觀景台"
+  },
+  "shopping_discounts_info": {
+    "brands": {
+      "title": "購物品牌查詢",
+      "choose": "請選擇品牌類別",
+      "jewely": "國際珠寶腕錶",
+      "boutique": "國際精品",
+      "skincare": "美妝保養品",
+      "apparel": "流行服飾",
+      "lifestyle": "生活居家/3C",
+      "cultural": "文化創意",
+      "signature": "特色品牌",
+      "outside": "館外店家"
+    },
+    "tourist_card": {
+      "title": "國際貴賓卡專屬禮遇",
+      "content": "即日起來台北101,提供2024年特別禮遇-申辦台北101國際貴賓卡,可享用國際旅客限定專屬三重好禮:<br>‧購物-品牌9折起特別優惠<br>‧禮遇-Welcome Pack+ NTD300現金折抵券<br>‧退稅-消費2000元以上可至B1退稅,並提供5%快速辦理服務<br><br>歡迎至購物中心B1客服中心填寫申請書,即可享有專屬優惠<br><br><a href='https://stage.taipei101mall.com.tw/?lang=zh&utm_source=101TTE_PH&utm_medium=Travel_Fair&utm_campaign=Stage' class='ar-link mb-3' target='_blank'>立即線上申辦</a>",
+      "apply": "立即線上申辦"
+    }
+  },
+  "location": {
+    "b1_din_tai_fung": "B1 鼎泰豐",
+    "b1_centre": "B1 中環",
+    "b1_elevator_no_2": "B1 2號電梯",
+    "b1_elevator_no_4": "B1 4號電梯",
+    "b1_elevator_no_3": "B1 3號電梯",
+    "1f_south_xinyi": "1F 信義環",
+    "1f_elevator_no_3": "1F 3號電梯",
+    "2f_elevator_no_3": "2F 3號電梯",
+    "88f_observatory": "88F 觀景台",
+    "89f_observatory": "89F 觀景台"
+  },
+  "navigation": {
+    "observatory_ticket_office": "觀景台售票處",
+    "atm": "ATM",
+    "items_and_luggage_storage": "置物櫃",
+    "metro_station": "捷運",
+    "taxi_station": "計程車乘車處",
+    "101f_queue": "101F 排隊處",
+    "91f_outdoor_area": "91F 戶外區",
+    "ice_warmed_water_dispenser": "飲水機",
+    "snackable_souvenirs": "伴手禮區",
+    "high_rise_restaurant_reception": "景觀餐廳櫃台",
+    "accessible_facilities": "無障礙廁所",
+    "tax_refund_currency_exchange": "退稅/外幣兌換櫃台",
+    "service_counter": "客服",
+    "service_counter_parking_discount": "客服(停車折抵)",
+    "restroom": "廁所",
+    "nursing_room": "哺乳室",
+    "food_court": "美食街",
+    "supermarket": "超市",
+    "din_tai_fung": "鼎泰豐",
+    "all_brands": "全館品牌"
+  },
+  "external": {
+    "gifts": "伴手禮",
+    "dining": "餐飲",
+    "accommodation": "住宿",
+    "hairdressing": "美髮",
+    "car_supplies": "汽車用品",
+    "entertainment": "休閒娛樂",
+    "pet_supplies": "寵物用品"
+  }
+}

+ 128 - 0
src/language/zh.json

@@ -0,0 +1,128 @@
+{
+  "title": "AI 智能客服",
+  "prologue": "您好,歡迎您使用台北101智能客服系統",
+  "service": "請問有什麼可以為您服務的呢?",
+  "ctaBuyNow": "立即購票",
+  "ctaGoUrl": "立即前往",
+  "customer_show": "叫出真人客服",
+  "customer_hide": "隱藏真人客服",
+  "observation_deck": "秘境花園觀景台",
+  "location_guide": "位置導引",
+  "food_souvenirs": "美食/伴手禮",
+  "shopping_discounts": "購物及優惠",
+  "service_information": "服務資訊",
+  "ar_tour": "進行 AR 導覽",
+  "select_location": "請選擇您的位置進行 AR 導覽",
+  "question": "點我問問題",
+  "qr_code_scan_prompt": "請掃描 QR Code 取得當前位置",
+  "tap_to_start_scan": "點我開啟掃描",
+  "current_location": "當前位置",
+  "select_navigation_location": "請選擇導覽位置",
+  "system_construction": "系統建置中,<br>關閉語音輸入功能。",
+  "tap_to_record": "點選以進行錄音",
+  "close": "關閉",
+  "service_info": {
+    "title": "服務資訊",
+    "inquiry_prompt": "請問您想查詢?<br>或於下方文字框輸入您的問題",
+    "business_hours": "營業時間",
+    "business_hours_1": "購物中心<br>周日至周四 11:00-21:30<br>周五、周六及國定假日前一天 11:00-22:00<br><br>觀景台<br>周一至周日及國定假日<br>10:00-21:00",
+    "tax_refund": "退稅",
+    "tax_refund_1": "★請持今日於館內累積購買超過新台幣 2,000 元以上可退稅商品之台北101發票及外籍護照(入台證)正本及購買商品至地下一樓退稅中心辦理。<br><br>▲ 須出示加蓋入境章之護照正本【中港澳旅客-僅能持入台證辦理】<br>▲ 若扣掉手續費,約退百分之3.8左右的稅金",
+    "toilets": "廁所/親子廁所",
+    "toilets_1": "洗手間所在位置於<br>地下一樓:信義環、中環、松智環<br>一樓至三樓:中環<br>四樓:松智環(維修中暫停使用)<br>五樓:松智環(靠近觀景台售票處)<br><br>台北101 購物中心親子廁所,位於地下一樓中環、松智環、信義環之男女廁所內,以及 3 樓中環男女廁旁。3 樓中環之獨立親子廁所,除一般洗手間功能外,另設有尿布檯、兒童安全座椅及兒童專用馬桶,空間寬敞且設施更加完善,方便家長及小朋友使用!",
+    "charge": "充電",
+    "charge_1": "請至B1中環顧客服務中心旁租借行動電源。"
+  },
+  "food_souvenirs_info": {
+    "searching": "請問您想查詢哪一種美食?",
+    "signature": "特色/高空餐廳",
+    "light": "輕食/CAFÉ",
+    "courts": "美食街",
+    "souvenir": "伴手禮"
+  },
+  "observatory_info": {
+    "tickets": {
+      "title": "線上購票",
+      "purchase": "請問您需要線上購票嗎?",
+      "id_card": "請問您是否持有中華民國身分證?",
+      "yes": "有",
+      "no": "沒有",
+      "ticket_type": "請問您要購買哪種門票?"
+    },
+    "visitor": {
+      "title": "參觀資訊",
+      "question": "請於下方文字框輸入您的問題:",
+      "faq": "常見問題提示:",
+      "events_q": "特色活動",
+      "events_a": "台北101觀景台是全台灣最高的觀景台,在382公尺的360度全視野高空俯瞰,高度不僅是象山的兩倍多,更是唯一可以眺望基隆河谷景觀的景點。目前台北101在89F觀景台打造夢幻打卡點,無論是「雲端上的秘境花園」、「天使之翼」、「雲中星月」等,無論是大人還是小孩,都能置身在仙境般的場景,令人陶醉其中。",
+      "ball_q": "風阻尼球",
+      "ball_a": "台北101風阻尼球的完整正式名稱:調諧質量阻尼器TMD (Tuned Mass Damper),是針對本大樓需求量身定做的被動阻尼系統,位置設置於87F~92F的樓層中央位置,主要目的為減低大樓受強風吹襲時之擺動,減少在大樓工作之人員感到不舒適之程度。有別於一般傳統隱藏式阻尼系統,台北101-風阻尼球的設計特別兼顧了功能及外觀,在觀景台將可一窺阻尼系統的整體運作。",
+      "accessible_q": "無障礙設施",
+      "accessible_a": "觀景台設有無障礙廁所,上下樓如果不方便用樓梯行動,也可請服務人員協助。"
+    },
+    "view": "實境景色",
+    "garden": "前往秘境花園觀景台"
+  },
+  "shopping_discounts_info": {
+    "brands": {
+      "title": "購物品牌查詢",
+      "choose": "請選擇品牌類別",
+      "jewely": "國際珠寶腕錶",
+      "boutique": "國際精品",
+      "skincare": "美妝保養品",
+      "apparel": "流行服飾",
+      "lifestyle": "生活居家/3C",
+      "cultural": "文化創意",
+      "signature": "特色品牌",
+      "outside": "館外店家"
+    },
+    "tourist_card": {
+      "title": "國際貴賓卡專屬禮遇",
+      "content": "即日起來台北101,提供2024年特別禮遇-申辦台北101國際貴賓卡,可享用國際旅客限定專屬三重好禮:<br>‧購物-品牌9折起特別優惠<br>‧禮遇-Welcome Pack+ NTD300現金折抵券<br>‧退稅-消費2000元以上可至B1退稅,並提供5%快速辦理服務<br><br>歡迎至購物中心B1客服中心填寫申請書,即可享有專屬優惠<br><br><a href='https://stage.taipei101mall.com.tw/join-member/AIsystem' class='ar-link mb-3' target='_blank'>立即線上申辦</a>",
+      "apply": "立即線上申辦"
+    }
+  },
+  "location": {
+    "b1_din_tai_fung": "B1 鼎泰豐",
+    "b1_centre": "B1 中環",
+    "b1_elevator_no_2": "B1 2號電梯",
+    "b1_elevator_no_4": "B1 4號電梯",
+    "b1_elevator_no_3": "B1 3號電梯",
+    "1f_south_xinyi": "1F 信義環",
+    "1f_elevator_no_3": "1F 3號電梯",
+    "2f_elevator_no_3": "2F 3號電梯",
+    "88f_observatory": "88F 觀景台",
+    "89f_observatory": "89F 觀景台"
+  },
+  "navigation": {
+    "observatory_ticket_office": "觀景台售票處",
+    "atm": "ATM",
+    "items_and_luggage_storage": "置物櫃",
+    "metro_station": "捷運",
+    "taxi_station": "計程車乘車處",
+    "101f_queue": "101F 排隊處",
+    "91f_outdoor_area": "91F 戶外區",
+    "ice_warmed_water_dispenser": "飲水機",
+    "snackable_souvenirs": "伴手禮區",
+    "high_rise_restaurant_reception": "景觀餐廳櫃台",
+    "accessible_facilities": "無障礙廁所",
+    "tax_refund_currency_exchange": "退稅/外幣兌換櫃台",
+    "service_counter": "客服",
+    "service_counter_parking_discount": "客服(停車折抵)",
+    "restroom": "廁所",
+    "nursing_room": "哺乳室",
+    "food_court": "美食街",
+    "supermarket": "超市",
+    "din_tai_fung": "鼎泰豐",
+    "all_brands": "全館品牌"
+  },
+  "external": {
+    "gifts": "伴手禮",
+    "dining": "餐飲",
+    "accommodation": "住宿",
+    "hairdressing": "美髮",
+    "car_supplies": "汽車用品",
+    "entertainment": "休閒娛樂",
+    "pet_supplies": "寵物用品"
+  }
+}

+ 30 - 0
src/main.js

@@ -0,0 +1,30 @@
+import './assets/main.css'
+
+import { createApp } from 'vue'
+import { createPinia } from 'pinia'
+
+import App from './App.vue'
+import router from './router'
+import i18n from './plugins/i18n'
+
+// Vuetify
+import vuetify from './plugins/vuetify'
+// import '@mdi/font/css/materialdesignicons.css'
+// import 'vuetify/styles'
+// import { createVuetify } from 'vuetify'
+// import * as components from 'vuetify/components'
+// import * as directives from 'vuetify/directives'
+
+// const vuetify = createVuetify({
+//     components,
+//     directives,
+// })
+
+const app = createApp(App)
+
+app.use(createPinia())
+app.use(router)
+app.use(vuetify)
+app.use(i18n)
+
+app.mount('#app')

+ 20 - 0
src/plugins/i18n.js

@@ -0,0 +1,20 @@
+import { createI18n } from 'vue-i18n';
+import zh from "../language/zh.json";
+import en from "../language/en.json";
+import ja from "../language/ja.json";
+import ko from "../language/ko.json";
+
+const i18n = createI18n({
+  legacy: false,
+  locale: localStorage.getItem("lang") ?? "zh",
+  fallbackLocale: "zh",
+  globalInjection: true,
+  messages: {
+    "zh": zh,
+    "en": en,
+    "ja": ja,
+    "ko": ko,
+  }
+})
+
+export default i18n

+ 28 - 0
src/plugins/vuetify.js

@@ -0,0 +1,28 @@
+import 'vuetify/styles'
+import { createVuetify } from 'vuetify'
+import * as components from 'vuetify/components'
+import * as directives from 'vuetify/directives'
+import { aliases, mdi } from 'vuetify/iconsets/mdi'
+import '@mdi/font/css/materialdesignicons.css'
+
+export default createVuetify({
+  components,
+  directives,
+  icons: {
+    defaultSet: 'mdi',
+    aliases,
+    sets: {
+      mdi,
+    },
+  },
+  theme: {
+    themes: {
+      light: {
+        dark: false,
+        colors: {
+          primary: '#b78c5f',
+        },
+      },
+    },
+  },
+})

+ 62 - 0
src/router/index.js

@@ -0,0 +1,62 @@
+import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
+import HomeView from '../views/HomeView.vue'
+import ArTourView from '../views/ArTourView.vue'
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes: [
+    {
+      path: '/',
+      name: 'home',
+      component: HomeView,
+    },
+    // 定位點網址設定
+    {
+      path: '/b1-1',
+      component: HomeView,
+    },
+    {
+      path: '/b1-2',
+      component: HomeView,
+    },
+    {
+      path: '/b1-3',
+      component: HomeView,
+    },
+    {
+      path: '/b1-4',
+      component: HomeView,
+    },
+    {
+      path: '/b1-5',
+      component: HomeView,
+    },
+    {
+      path: '/1f-6',
+      component: HomeView,
+    },
+    {
+      path: '/1f-7',
+      component: HomeView,
+    },
+    {
+      path: '/2f-8',
+      component: HomeView,
+    },
+    {
+      path: '/89f-9',
+      component: HomeView,
+    },
+    {
+      path: '/88f-10',
+      component: HomeView,
+    },
+    {
+      path: '/ar-tour',
+      name: 'ar-tour',
+      component: ArTourView
+    },
+  ]
+})
+
+export default router

+ 12 - 0
src/stores/counter.js

@@ -0,0 +1,12 @@
+import { ref, computed } from 'vue'
+import { defineStore } from 'pinia'
+
+export const useCounterStore = defineStore('counter', () => {
+  const count = ref(0)
+  const doubleCount = computed(() => count.value * 2)
+  function increment() {
+    count.value++
+  }
+
+  return { count, doubleCount, increment }
+})

+ 119 - 0
src/views/ArTourView.vue

@@ -0,0 +1,119 @@
+<script setup>
+import { reactive } from "vue";
+import Navbar from "../components/Navbar.vue";
+
+let guideList = reactive([
+  {
+    name: "廁所 / 無障礙廁所",
+    value: "toilet",
+  },
+  {
+    name: "客服",
+    value: "service",
+  },
+  {
+    name: "哺乳室",
+    value: "nursing-room",
+  },
+  {
+    name: "美食街",
+    value: "food",
+  },
+  {
+    name: "鼎泰豐",
+    value: "din-tai-fung",
+  },
+  {
+    name: "超市 / 伴手禮區",
+    value: "market",
+  },
+]);
+
+function getImagePath(image) {
+  const imgUrl = `../src/assets/img/icon/${image}`;
+  return imgUrl;
+}
+</script>
+
+<template>
+  <Navbar />
+  <v-container class="mt-10 main-content">
+    <v-row class="d-flex align-center">
+      <v-col cols="12" lg="9">
+        <v-sheet class="pa-2 ma-2">
+          <img src="@/assets/img/map/b1.svg" alt="" class="map-img" />
+        </v-sheet>
+      </v-col>
+      <v-col cols="12" lg="3">
+        <v-sheet class="pa-2 ma-2">
+          <p class="headline">選擇導覽點</p>
+          <ul class="guide-list">
+            <li v-for="item in guideList" :key="item.value">
+              <!-- <img :src="getImagePath(item.img)" alt="" class="icon" /> -->
+              <button>
+                <p>{{ item.name }}</p>
+              </button>
+            </li>
+          </ul>
+        </v-sheet>
+      </v-col>
+    </v-row>
+  </v-container>
+</template>
+
+<style lang="scss" scoped>
+.main-content {
+  display: flex;
+  align-items: center;
+
+  .map-img {
+    width: 100%;
+    max-height: 100vh;
+  }
+
+  .guide-list {
+    margin-top: 1.5rem;
+    list-style: none;
+
+    li {
+      margin-bottom: 1rem;
+      display: flex;
+      align-items: center;
+      border: 0.01rem solid var(--main-color);
+
+      button {
+        width: 100%;
+        padding: 0.5rem;
+        transition: all 0.3s;
+
+        &:hover {
+          background-color: var(--main-color);
+
+          p {
+            color: white;
+          }
+        }
+
+        p {
+          color: var(--main-color);
+          letter-spacing: 0.3rem;
+          text-align: center;
+          transition: all 0.3s;
+        }
+      }
+    }
+
+    .icon {
+      width: 2rem;
+      margin-right: 0.8rem;
+    }
+  }
+
+  .headline {
+    text-align: center;
+    font-size: 1.25rem;
+    font-weight: 500;
+    letter-spacing: 0.1rem;
+  }
+}
+</style>

+ 26 - 0
src/views/HomeView.vue

@@ -0,0 +1,26 @@
+<script setup>
+import Chat from "../components/Chat.vue";
+import Navbar from "../components/Navbar.vue";
+</script>
+
+<template>
+  <Navbar />
+  <main>
+    <Chat />
+  </main>
+</template>
+
+<style scoped>
+main {
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  background-color: rgba(0, 0, 0, 0.6);
+  background-blend-mode: multiply;
+  background-image: url("@/assets/img/banner.jpg");
+  background-size: cover;
+  background-position: center center;
+}
+</style>

+ 16 - 0
vite.config.js

@@ -0,0 +1,16 @@
+import { fileURLToPath, URL } from 'node:url'
+
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    vue(),
+  ],
+  resolve: {
+    alias: {
+      '@': fileURLToPath(new URL('./src', import.meta.url))
+    }
+  }
+})

Some files were not shown because too many files changed in this diff