Jerry Liang 1 år sedan
incheckning
25efa8449a
100 ändrade filer med 16451 tillägg och 0 borttagningar
  1. 9 0
      .editorconfig
  2. 3 0
      .env
  3. 24 0
      .env.development
  4. 26 0
      .env.production
  5. 1 0
      .gitattributes
  6. 33 0
      .gitignore
  7. 6 0
      .prettierrc.json
  8. 12 0
      .vscode/extensions.json
  9. 64 0
      README.md
  10. 4 0
      e2e/tsconfig.json
  11. 8 0
      e2e/vue.spec.ts
  12. 26 0
      env.d.ts
  13. 61 0
      eslint.config.ts
  14. 26 0
      index.html
  15. 97 0
      package.json
  16. 110 0
      playwright.config.ts
  17. 9974 0
      pnpm-lock.yaml
  18. 3 0
      pnpm-workspace.yaml
  19. BIN
      public/android-chrome-192x192.png
  20. BIN
      public/android-chrome-512x512.png
  21. BIN
      public/apple-touch-icon.png
  22. 1 0
      public/assets/preloader.css
  23. BIN
      public/favicon-16x16.png
  24. BIN
      public/favicon-32x32.png
  25. BIN
      public/favicon-vue.ico
  26. BIN
      public/favicon.ico
  27. 11 0
      public/site.webmanifest
  28. 6 0
      service/.env
  29. 0 0
      service/.vscode/settings.json
  30. 69 0
      service/README.md
  31. 30 0
      service/cache/cache.go
  32. 52 0
      service/cmd/deploy.sh
  33. 69 0
      service/cmd/gen/fwh_pro.go
  34. 65 0
      service/config/config.go
  35. 3 0
      service/controllers/controllers.go
  36. 96 0
      service/controllers/mysql_controllers.go
  37. 356 0
      service/controllers/sitesMonitorControllers.go
  38. 18 0
      service/database/database.go
  39. 43 0
      service/database/mongo.go
  40. 32 0
      service/database/mysql.go
  41. 25 0
      service/database/pgsql.go
  42. 73 0
      service/database/sql.md
  43. BIN
      service/database/sql.png
  44. 147 0
      service/database/sql/home_energy_storage_server_iot_gateway_2025-08-11_152722.sql
  45. 23 0
      service/database/sql/queryDev.sql
  46. 1 0
      service/database/sql/queryDistinct.sql
  47. 1 0
      service/docs/docs.go
  48. 300 0
      service/ext/api/fwh_pro/fwh_pro.go
  49. 81 0
      service/go.mod
  50. 235 0
      service/go.sum
  51. 17 0
      service/logger/logger.go
  52. 37 0
      service/main.go
  53. 100 0
      service/models/fwh_pro/model/iot_gateway.gen.go
  54. 103 0
      service/models/fwh_pro/query/gen.go
  55. 710 0
      service/models/fwh_pro/query/iot_gateway.gen.go
  56. 75 0
      service/routes/routes.go
  57. 184 0
      service/routes/sitesMonitorRoutes.go
  58. 16 0
      service/scheduler/scheduler.go
  59. 55 0
      src/App.vue
  60. 30 0
      src/assets/base.css
  61. 13 0
      src/assets/main.css
  62. BIN
      src/assets/noise.png
  63. BIN
      src/assets/texture.png
  64. 0 0
      src/assets/topography.svg
  65. 64 0
      src/components/Avatar.vue
  66. 112 0
      src/components/ButtonAnimation.vue
  67. 41 0
      src/components/EmptyPlaceholder.vue
  68. 36 0
      src/components/HintHelp.vue
  69. 15 0
      src/components/Noise.vue
  70. 80 0
      src/components/__tests__/comp.test.ts
  71. 9 0
      src/components/index.ts
  72. 56 0
      src/composable/comp/useComponentModifier.ts
  73. 72 0
      src/composable/comp/useComponentThemeOverrides.ts
  74. 110 0
      src/composable/comp/useDataTable.ts
  75. 36 0
      src/composable/useDiscreteApi.ts
  76. 88 0
      src/composable/usePersonalization.ts
  77. 53 0
      src/composable/useResettable.ts
  78. 57 0
      src/composable/useTheme.ts
  79. 8 0
      src/injection/index.ts
  80. 13 0
      src/injection/interface.ts
  81. 44 0
      src/layout/aside/component/Menu.vue
  82. 90 0
      src/layout/aside/component/UserCard.vue
  83. 45 0
      src/layout/aside/index.vue
  84. 414 0
      src/layout/component/Tabs.vue
  85. 15 0
      src/layout/footer/index.vue
  86. 259 0
      src/layout/header/Actions/component/ConfigureDrawer.vue
  87. 20 0
      src/layout/header/Actions/component/FullScreen.vue
  88. 42 0
      src/layout/header/Actions/component/NoiseModal.vue
  89. 32 0
      src/layout/header/Actions/component/SignOut.vue
  90. 75 0
      src/layout/header/Actions/component/ThemeDropdown.vue
  91. 275 0
      src/layout/header/Actions/component/WatermarkModal.vue
  92. 23 0
      src/layout/header/Actions/index.vue
  93. 86 0
      src/layout/header/Breadcrumb.vue
  94. 41 0
      src/layout/header/Logo.vue
  95. 49 0
      src/layout/header/Navigation.vue
  96. 41 0
      src/layout/header/index.vue
  97. 98 0
      src/layout/index.vue
  98. 302 0
      src/layout/main/index.vue
  99. 42 0
      src/main.ts
  100. 44 0
      src/main.ts.bak

+ 9 - 0
.editorconfig

@@ -0,0 +1,9 @@
+[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
+charset = utf-8
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+end_of_line = lf
+max_line_length = 100

+ 3 - 0
.env

@@ -0,0 +1,3 @@
+# title
+VITE_GLOB_APP_TITLE = FranklinWH Toolbox
+VITE_GLOB_APP_NAME = FWH Toolbox

+ 24 - 0
.env.development

@@ -0,0 +1,24 @@
+# 本地环境
+VITE_USER_NODE_ENV = development
+
+# 公共基础路径
+VITE_PUBLIC_PATH = /fwh-toolbox/
+
+# 本地运行端口号
+VITE_PORT = 8363
+
+# 开启 devTools 调试
+VITE_DEVTOOLS = true
+
+# 开启 CodeInspector 调试
+VITE_CODEINSPECTOR = true
+
+# 路由模式
+# Optional: hash | history
+VITE_ROUTER_MODE = hash
+
+# 开发环境接口地址
+VITE_API_URL = /fwh-toolbox/api
+
+# 启动时自动打开浏览器
+VITE_OPEN = false

+ 26 - 0
.env.production

@@ -0,0 +1,26 @@
+# 线上环境
+VITE_USER_NODE_ENV = production
+
+# 公共基础路径
+VITE_PUBLIC_PATH = /fwh-toolbox/
+
+# 路由模式
+# Optional: hash | history
+VITE_ROUTER_MODE = hash
+
+# 打包后是否生成包分析文件
+VITE_REPORT = false
+
+# 是否启用 gzip 或 brotli 压缩打包,如果需要多个压缩规则,可以使用 “,” 分隔
+# Optional: gzip | brotli | none
+VITE_BUILD_COMPRESS = none
+
+# 打包压缩后是否删除源文件
+VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
+
+# 打包时是否删除 console
+VITE_DROP_CONSOLE = true
+
+# 线上环境接口地址
+VITE_API_URL = /fwh-toolbox/api
+# VITE_API_URL = "https://mock.mengxuegu.com/mock/629d727e6163854a32e8307e"

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+* text=auto eol=lf

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+# 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
+
+test-results/
+playwright-report/

+ 6 - 0
.prettierrc.json

@@ -0,0 +1,6 @@
+{
+  "$schema": "https://json.schemastore.org/prettierrc",
+  "semi": false,
+  "singleQuote": true,
+  "printWidth": 100
+}

+ 12 - 0
.vscode/extensions.json

@@ -0,0 +1,12 @@
+{
+  "recommendations": [
+    "Vue.volar",
+    "vitest.explorer",
+    "ms-playwright.playwright",
+    "dbaeumer.vscode-eslint",
+    "EditorConfig.EditorConfig",
+    "esbenp.prettier-vscode",
+    "antfu.unocss",
+    "antfu.iconify"
+  ]
+}

+ 64 - 0
README.md

@@ -0,0 +1,64 @@
+# FranklinWH-ToolBox
+
+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).
+
+## Type Support for `.vue` Imports in TS
+
+TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vite.dev/config/).
+
+## Project Setup
+
+```sh
+npm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+npm run dev
+```
+
+### Type-Check, Compile and Minify for Production
+
+```sh
+npm run build
+```
+
+### Run Unit Tests with [Vitest](https://vitest.dev/)
+
+```sh
+npm run test:unit
+```
+
+### Run End-to-End Tests with [Playwright](https://playwright.dev)
+
+```sh
+# Install browsers for the first run
+npx playwright install
+
+# When testing on CI, must build the project first
+npm run build
+
+# Runs the end-to-end tests
+npm run test:e2e
+# Runs the tests only on Chromium
+npm run test:e2e -- --project=chromium
+# Runs the tests of a specific file
+npm run test:e2e -- tests/example.spec.ts
+# Runs the tests in debug mode
+npm run test:e2e -- --debug
+```
+
+### Lint with [ESLint](https://eslint.org/)
+
+```sh
+npm run lint
+```

+ 4 - 0
e2e/tsconfig.json

@@ -0,0 +1,4 @@
+{
+  "extends": "@tsconfig/node22/tsconfig.json",
+  "include": ["./**/*"]
+}

+ 8 - 0
e2e/vue.spec.ts

@@ -0,0 +1,8 @@
+import { test, expect } from '@playwright/test';
+
+// See here how to get started:
+// https://playwright.dev/docs/intro
+test('visits the app root url', async ({ page }) => {
+  await page.goto('/');
+  await expect(page.locator('h1')).toHaveText('You did it!');
+})

+ 26 - 0
env.d.ts

@@ -0,0 +1,26 @@
+/// <reference types="vite/client" />
+
+export interface ImportMetaEnv {
+  readonly VITE_GLOB_APP_TITLE: string // APP显示标题
+  readonly VITE_GLOB_APP_NAME: string // APP名称
+  readonly VITE_USER_NODE_ENV: string // 运行环境:"development" 或 "production"
+  readonly VITE_PUBLIC_PATH: string // 公共基础路径
+  readonly VITE_ROUTER_MODE: string // 路由模式 Optional: hash | history
+  readonly VITE_API_URL: string // API接口基地址
+
+  // 仅development环境生效
+  readonly VITE_PORT?: string // APP调试运行端口号
+  readonly VITE_OPEN?: string // 启动时自动打开浏览器 true or false
+  readonly VITE_DEVTOOLS?: string // 开启 devTools 调试 true or false
+  readonly VITE_CODEINSPECTOR?: string // 开启 CodeInspector 调试 true or false
+
+  // 仅production打包
+  readonly VITE_REPORT?: string // 打包后是否生成包分析文件 true or false
+  readonly VITE_DROP_CONSOLE?: string // 打包时是否删除 console true or false
+  readonly VITE_BUILD_COMPRESS?: string // 是否启用 gzip 或 brotli 压缩打包,如果需要多个压缩规则,可以使用 “,” 分隔 Optional: gzip | brotli | none
+  readonly VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE?: string // 打包压缩后是否删除源文件 true or false
+}
+
+interface ImportMeta {
+  readonly env: ImportMetaEnv
+}

+ 61 - 0
eslint.config.ts

@@ -0,0 +1,61 @@
+import antfu from '@antfu/eslint-config'
+import pluginVitest from '@vitest/eslint-plugin'
+import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
+import pluginPlaywright from 'eslint-plugin-playwright'
+import pluginVue from 'eslint-plugin-vue'
+
+// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
+// import { configureVueProject } from '@vue/eslint-config-typescript'
+// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
+// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
+
+export default antfu(
+  {
+    type: 'app',
+    vue: true, // 启用 Vue 支持
+    typescript: true, // 启用 TS 支持
+    jsonc: true, // 支持 .json/.jsonc
+    yaml: true, // 支持 .yaml
+    markdown: true, // 支持 .md 中代码块
+    test: true, // 启用 Vitest 支持
+    stylistic: {
+      indent: 2,
+      semi: false,
+      quotes: 'single',
+      jsx: true,
+    },
+    ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
+    rules: {
+      'vue/multi-word-component-names': 'off', // 🚫 禁用多单词组件名规则
+    },
+  },
+
+  pluginVue.configs['flat/essential'],
+
+  // ✅ 添加 vue/block-lang 规则
+  {
+    rules: {
+      'vue/block-lang': [
+        'error',
+        {
+          script: {
+            lang: ['ts', 'tsx'], // ✅ 支持 tsx
+          },
+        },
+      ],
+    },
+  },
+
+  // ✅ 测试文件配置
+  {
+    ...pluginVitest.configs.recommended,
+    files: ['src/**/__tests__/*'],
+  },
+
+  // ✅ Playwright 配置
+  {
+    ...pluginPlaywright.configs['flat/recommended'],
+    files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'],
+  },
+  skipFormatting,
+)

+ 26 - 0
index.html

@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <link href="/assets/preloader.css" rel="stylesheet" />
+    <title>%VITE_GLOB_APP_TITLE%</title>
+  </head>
+  <body>
+    <div id="appLoader">
+      <div id="clock"></div>
+    </div>
+    <div id="app"></div>
+    <script>
+      document.documentElement.classList.toggle(
+        'dark',
+        localStorage.getItem('theme') === 'dark' ||
+          (!localStorage.getItem('theme') &&
+            window.matchMedia('(prefers-color-scheme: dark)').matches),
+      )
+      window.loaderElement = document.getElementById('appLoader')
+    </script>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 97 - 0
package.json

@@ -0,0 +1,97 @@
+{
+  "name": "fwh-toolbox",
+  "version": "0.0.0",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "rm -rf ./dist && run-p type-check \"build-only {@}\" --",
+    "preview": "vite preview",
+    "test:unit": "vitest",
+    "test:e2e": "playwright test",
+    "test:ui": "vitest --ui",
+    "build-only": "vite build",
+    "type-check": "vue-tsc --build",
+    "lint": "eslint src/",
+    "lint:fix": "eslint src/ --fix",
+    "lint:isp": "eslint --inspect-config",
+    "format:check": "prettier --check src/",
+    "format:fix": "prettier --write src/",
+    "modules:isp": "node-modules-inspector",
+    "dedupe": "pnpm dedupe",
+    "install": "rm -rf /var/www/fwh-toolbox/fwh-toolbox/* && cp -rf ./dist/* /var/www/fwh-toolbox/fwh-toolbox/"
+  },
+  "dependencies": {
+    "@antv/g-webgl": "^2.0.51",
+    "@antv/g2": "^5.3.4",
+    "@dotenvx/dotenvx": "^1.48.0",
+    "@iconify-json/carbon": "^1.2.13",
+    "@iconify-json/fluent": "^1.2.28",
+    "@iconify-json/hugeicons": "^1.2.6",
+    "@iconify-json/mage": "^1.2.2",
+    "@iconify-json/material-symbols": "^1.2.30",
+    "@iconify-json/mdi": "^1.2.3",
+    "@iconify-json/ph": "^1.2.2",
+    "@iconify-json/pixelarticons": "^1.2.2",
+    "@iconify-json/prime": "^1.2.2",
+    "@iconify-json/streamline-freehand": "^1.2.1",
+    "@unocss/reset": "^66.3.3",
+    "@unocss/transformer-directives": "^66.3.3",
+    "@unocss/transformer-variant-group": "^66.3.3",
+    "@vueuse/components": "^13.5.0",
+    "@vueuse/core": "^13.5.0",
+    "chroma-js": "^3.1.2",
+    "es-toolkit": "^1.39.8",
+    "naive-ui": "^2.42.0",
+    "pinia": "^3.0.3",
+    "pinia-plugin-persistedstate": "^4.4.1",
+    "uuid": "^11.1.0",
+    "vue": "^3.5.18",
+    "vue-draggable-plus": "^0.6.0",
+    "vue-router": "^4.5.1"
+  },
+  "devDependencies": {
+    "@antfu/eslint-config": "^5.0.0",
+    "@playwright/test": "^1.54.1",
+    "@tsconfig/node22": "^22.0.2",
+    "@types/chroma-js": "^3.1.1",
+    "@types/jsdom": "^21.1.7",
+    "@types/node": "^22.16.4",
+    "@vitejs/plugin-vue": "^6.0.0",
+    "@vitejs/plugin-vue-jsx": "^5.0.1",
+    "@vitest/eslint-plugin": "^1.3.4",
+    "@vitest/ui": "^3.2.4",
+    "@vue/eslint-config-prettier": "^10.2.0",
+    "@vue/test-utils": "^2.4.6",
+    "@vue/tsconfig": "^0.7.0",
+    "eslint": "^9.31.0",
+    "eslint-plugin-playwright": "^2.2.0",
+    "eslint-plugin-vue": "~10.2.0",
+    "jiti": "^2.4.2",
+    "jsdom": "^26.1.0",
+    "node-modules-inspector": "^1.0.0",
+    "npm-run-all2": "^8.0.4",
+    "prettier": "3.5.3",
+    "sass-embedded": "^1.89.2",
+    "typescript": "~5.8.3",
+    "unocss": "^66.3.3",
+    "unplugin-auto-import": "^20.0.0",
+    "unplugin-vue-components": "^28.8.0",
+    "vite": "npm:rolldown-vite@^7.0.9",
+    "vite-plugin-compression": "^0.5.1",
+    "vite-plugin-html": "^3.2.2",
+    "vite-plugin-vue-devtools": "^7.7.7",
+    "vitest": "^3.2.4",
+    "vue-macros": "3.0.0-beta.21",
+    "vue-tsc": "^3.0.4",
+    "vue-vine-tsc": "^1.6.5"
+  },
+  "pnpm": {
+    "overrides": {
+      "vite-plugin-vue-devtools>vite-plugin-inspect": "^11.3.0"
+    }
+  },
+  "overrides": {
+    "vite-plugin-vue-devtools>vite-plugin-inspect": "^11.3.0"
+  }
+}

+ 110 - 0
playwright.config.ts

@@ -0,0 +1,110 @@
+import process from 'node:process'
+import { defineConfig, devices } from '@playwright/test'
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+  testDir: './e2e',
+  /* Maximum time one test can run for. */
+  timeout: 30 * 1000,
+  expect: {
+    /**
+     * Maximum time expect() should wait for the condition to be met.
+     * For example in `await expect(locator).toHaveText();`
+     */
+    timeout: 5000,
+  },
+  /* Fail the build on CI if you accidentally left test.only in the source code. */
+  forbidOnly: !!process.env.CI,
+  /* Retry on CI only */
+  retries: process.env.CI ? 2 : 0,
+  /* Opt out of parallel tests on CI. */
+  workers: process.env.CI ? 1 : undefined,
+  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+  reporter: 'html',
+  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+  use: {
+    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+    actionTimeout: 0,
+    /* Base URL to use in actions like `await page.goto('/')`. */
+    baseURL: process.env.CI ? 'http://localhost:4173' : 'http://localhost:5173',
+
+    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+    trace: 'on-first-retry',
+
+    /* Only on CI systems run the tests headless */
+    headless: !!process.env.CI,
+  },
+
+  /* Configure projects for major browsers */
+  projects: [
+    {
+      name: 'chromium',
+      use: {
+        ...devices['Desktop Chrome'],
+      },
+    },
+    {
+      name: 'firefox',
+      use: {
+        ...devices['Desktop Firefox'],
+      },
+    },
+    {
+      name: 'webkit',
+      use: {
+        ...devices['Desktop Safari'],
+      },
+    },
+
+    /* Test against mobile viewports. */
+    // {
+    //   name: 'Mobile Chrome',
+    //   use: {
+    //     ...devices['Pixel 5'],
+    //   },
+    // },
+    // {
+    //   name: 'Mobile Safari',
+    //   use: {
+    //     ...devices['iPhone 12'],
+    //   },
+    // },
+
+    /* Test against branded browsers. */
+    // {
+    //   name: 'Microsoft Edge',
+    //   use: {
+    //     channel: 'msedge',
+    //   },
+    // },
+    // {
+    //   name: 'Google Chrome',
+    //   use: {
+    //     channel: 'chrome',
+    //   },
+    // },
+  ],
+
+  /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+  // outputDir: 'test-results/',
+
+  /* Run your local dev server before starting the tests */
+  webServer: {
+    /**
+     * Use the dev server by default for faster feedback loop.
+     * Use the preview server on CI for more realistic testing.
+     * Playwright will re-use the local server if there is already a dev-server running.
+     */
+    command: process.env.CI ? 'npm run preview' : 'npm run dev',
+    port: process.env.CI ? 4173 : 5173,
+    reuseExistingServer: !process.env.CI,
+  },
+})

+ 9974 - 0
pnpm-lock.yaml

@@ -0,0 +1,9974 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+overrides:
+  vite-plugin-vue-devtools>vite-plugin-inspect: ^11.3.0
+
+importers:
+
+  .:
+    dependencies:
+      '@antv/g-webgl':
+        specifier: ^2.0.51
+        version: 2.0.52
+      '@antv/g2':
+        specifier: ^5.3.4
+        version: 5.3.5
+      '@dotenvx/dotenvx':
+        specifier: ^1.48.0
+        version: 1.48.4
+      '@iconify-json/carbon':
+        specifier: ^1.2.13
+        version: 1.2.13
+      '@iconify-json/fluent':
+        specifier: ^1.2.28
+        version: 1.2.28
+      '@iconify-json/hugeicons':
+        specifier: ^1.2.6
+        version: 1.2.6
+      '@iconify-json/mage':
+        specifier: ^1.2.2
+        version: 1.2.2
+      '@iconify-json/material-symbols':
+        specifier: ^1.2.30
+        version: 1.2.30
+      '@iconify-json/mdi':
+        specifier: ^1.2.3
+        version: 1.2.3
+      '@iconify-json/ph':
+        specifier: ^1.2.2
+        version: 1.2.2
+      '@iconify-json/pixelarticons':
+        specifier: ^1.2.2
+        version: 1.2.2
+      '@iconify-json/prime':
+        specifier: ^1.2.2
+        version: 1.2.2
+      '@iconify-json/streamline-freehand':
+        specifier: ^1.2.1
+        version: 1.2.1
+      '@unocss/reset':
+        specifier: ^66.3.3
+        version: 66.4.1
+      '@unocss/transformer-directives':
+        specifier: ^66.3.3
+        version: 66.4.1
+      '@unocss/transformer-variant-group':
+        specifier: ^66.3.3
+        version: 66.4.1
+      '@vueuse/components':
+        specifier: ^13.5.0
+        version: 13.6.0(vue@3.5.18(typescript@5.8.3))
+      '@vueuse/core':
+        specifier: ^13.5.0
+        version: 13.6.0(vue@3.5.18(typescript@5.8.3))
+      chroma-js:
+        specifier: ^3.1.2
+        version: 3.1.2
+      es-toolkit:
+        specifier: ^1.39.8
+        version: 1.39.8
+      naive-ui:
+        specifier: ^2.42.0
+        version: 2.42.0(vue@3.5.18(typescript@5.8.3))
+      pinia:
+        specifier: ^3.0.3
+        version: 3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))
+      pinia-plugin-persistedstate:
+        specifier: ^4.4.1
+        version: 4.4.1(pinia@3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3)))
+      uuid:
+        specifier: ^11.1.0
+        version: 11.1.0
+      vue:
+        specifier: ^3.5.18
+        version: 3.5.18(typescript@5.8.3)
+      vue-draggable-plus:
+        specifier: ^0.6.0
+        version: 0.6.0(@types/sortablejs@1.15.8)
+      vue-router:
+        specifier: ^4.5.1
+        version: 4.5.1(vue@3.5.18(typescript@5.8.3))
+    devDependencies:
+      '@antfu/eslint-config':
+        specifier: ^5.0.0
+        version: 5.2.0(@vue/compiler-sfc@3.5.18)(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4)
+      '@playwright/test':
+        specifier: ^1.54.1
+        version: 1.54.2
+      '@tsconfig/node22':
+        specifier: ^22.0.2
+        version: 22.0.2
+      '@types/chroma-js':
+        specifier: ^3.1.1
+        version: 3.1.1
+      '@types/jsdom':
+        specifier: ^21.1.7
+        version: 21.1.7
+      '@types/node':
+        specifier: ^22.16.4
+        version: 22.17.0
+      '@vitejs/plugin-vue':
+        specifier: ^6.0.0
+        version: 6.0.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))
+      '@vitejs/plugin-vue-jsx':
+        specifier: ^5.0.1
+        version: 5.0.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))
+      '@vitest/eslint-plugin':
+        specifier: ^1.3.4
+        version: 1.3.4(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4)
+      '@vitest/ui':
+        specifier: ^3.2.4
+        version: 3.2.4(vitest@3.2.4)
+      '@vue/eslint-config-prettier':
+        specifier: ^10.2.0
+        version: 10.2.0(eslint@9.32.0(jiti@2.5.1))(prettier@3.5.3)
+      '@vue/test-utils':
+        specifier: ^2.4.6
+        version: 2.4.6
+      '@vue/tsconfig':
+        specifier: ^0.7.0
+        version: 0.7.0(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))
+      eslint:
+        specifier: ^9.31.0
+        version: 9.32.0(jiti@2.5.1)
+      eslint-plugin-playwright:
+        specifier: ^2.2.0
+        version: 2.2.2(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-vue:
+        specifier: ~10.2.0
+        version: 10.2.0(eslint@9.32.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.32.0(jiti@2.5.1)))
+      jiti:
+        specifier: ^2.4.2
+        version: 2.5.1
+      jsdom:
+        specifier: ^26.1.0
+        version: 26.1.0
+      node-modules-inspector:
+        specifier: ^1.0.0
+        version: 1.0.0
+      npm-run-all2:
+        specifier: ^8.0.4
+        version: 8.0.4
+      prettier:
+        specifier: 3.5.3
+        version: 3.5.3
+      sass-embedded:
+        specifier: ^1.89.2
+        version: 1.90.0
+      typescript:
+        specifier: ~5.8.3
+        version: 5.8.3
+      unocss:
+        specifier: ^66.3.3
+        version: 66.4.1(postcss@8.5.6)(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+      unplugin-auto-import:
+        specifier: ^20.0.0
+        version: 20.0.0(@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3)))
+      unplugin-vue-components:
+        specifier: ^28.8.0
+        version: 28.8.0(@babel/parser@7.28.0)(vue@3.5.18(typescript@5.8.3))
+      vite:
+        specifier: npm:rolldown-vite@^7.0.9
+        version: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vite-plugin-compression:
+        specifier: ^0.5.1
+        version: 0.5.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+      vite-plugin-html:
+        specifier: ^3.2.2
+        version: 3.2.2(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+      vite-plugin-vue-devtools:
+        specifier: ^7.7.7
+        version: 7.7.7(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))
+      vitest:
+        specifier: ^3.2.4
+        version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.0)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vue-macros:
+        specifier: 3.0.0-beta.21
+        version: 3.0.0-beta.21(@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3)))(esbuild@0.25.8)(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(rollup@4.46.2)(typescript@5.8.3)(vue-tsc@3.0.5(typescript@5.8.3))(vue@3.5.18(typescript@5.8.3))
+      vue-tsc:
+        specifier: ^3.0.4
+        version: 3.0.5(typescript@5.8.3)
+      vue-vine-tsc:
+        specifier: ^1.6.5
+        version: 1.7.0(typescript@5.8.3)
+
+packages:
+
+  '@ampproject/remapping@2.3.0':
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+
+  '@antfu/eslint-config@5.2.0':
+    resolution: {integrity: sha512-qAf+Ja5COyF0414kUxY/BfyqyLaWrf1YTwUqMuUASPltugDc25t7OCQYNt1p/QilmVocCpsL1dUJWkehemidOA==}
+    hasBin: true
+    peerDependencies:
+      '@eslint-react/eslint-plugin': ^1.38.4
+      '@next/eslint-plugin-next': ^15.4.0-canary.115
+      '@prettier/plugin-xml': ^3.4.1
+      '@unocss/eslint-plugin': '>=0.50.0'
+      astro-eslint-parser: ^1.0.2
+      eslint: ^9.10.0
+      eslint-plugin-astro: ^1.2.0
+      eslint-plugin-format: '>=0.1.0'
+      eslint-plugin-jsx-a11y: '>=6.10.2'
+      eslint-plugin-react-hooks: ^5.2.0
+      eslint-plugin-react-refresh: ^0.4.19
+      eslint-plugin-solid: ^0.14.3
+      eslint-plugin-svelte: '>=2.35.1'
+      eslint-plugin-vuejs-accessibility: ^2.4.1
+      prettier-plugin-astro: ^0.14.0
+      prettier-plugin-slidev: ^1.0.5
+      svelte-eslint-parser: '>=0.37.0'
+    peerDependenciesMeta:
+      '@eslint-react/eslint-plugin':
+        optional: true
+      '@next/eslint-plugin-next':
+        optional: true
+      '@prettier/plugin-xml':
+        optional: true
+      '@unocss/eslint-plugin':
+        optional: true
+      astro-eslint-parser:
+        optional: true
+      eslint-plugin-astro:
+        optional: true
+      eslint-plugin-format:
+        optional: true
+      eslint-plugin-jsx-a11y:
+        optional: true
+      eslint-plugin-react-hooks:
+        optional: true
+      eslint-plugin-react-refresh:
+        optional: true
+      eslint-plugin-solid:
+        optional: true
+      eslint-plugin-svelte:
+        optional: true
+      eslint-plugin-vuejs-accessibility:
+        optional: true
+      prettier-plugin-astro:
+        optional: true
+      prettier-plugin-slidev:
+        optional: true
+      svelte-eslint-parser:
+        optional: true
+
+  '@antfu/install-pkg@1.1.0':
+    resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
+
+  '@antfu/utils@8.1.1':
+    resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
+
+  '@antv/component@2.1.5':
+    resolution: {integrity: sha512-+Pqu6CLkGTvYZw+UQK03B/yiR0h2Zu65tyUGUQkNuPv49Zwe4WqDZqqu94bqT1F07OAAVjeXLCTvhBb954DLXQ==}
+
+  '@antv/coord@0.4.7':
+    resolution: {integrity: sha512-UTbrMLhwJUkKzqJx5KFnSRpU3BqrdLORJbwUbHK2zHSCT3q3bjcFA//ZYLVfIlwqFDXp/hzfMyRtp0c77A9ZVA==}
+
+  '@antv/event-emitter@0.1.3':
+    resolution: {integrity: sha512-4ddpsiHN9Pd4UIlWuKVK1C4IiZIdbwQvy9i7DUSI3xNJ89FPUFt8lxDYj8GzzfdllV0NkJTRxnG+FvLk0llidg==}
+
+  '@antv/expr@1.0.2':
+    resolution: {integrity: sha512-vrfdmPHkTuiS5voVutKl2l06w1ihBh9A8SFdQPEE+2KMVpkymzGOF1eWpfkbGZ7tiFE15GodVdhhHomD/hdIwg==}
+
+  '@antv/g-camera-api@2.0.41':
+    resolution: {integrity: sha512-dF52/wpzHDKi7ZzPlaHurEjWrF9aBKL2udDwQkEeVtfkJ0DHaavr3BAvhuGhtHoecRYQJvpzP1OkGNDLQJQQlw==}
+
+  '@antv/g-canvas@2.0.48':
+    resolution: {integrity: sha512-P98cTLRbKbCAcUVgHqMjKcvOany6nR7wvt+g+sazIfKSMUCWgjLTOjlLezux2up3At29mt80StaV2AR3d61YQA==}
+
+  '@antv/g-device-api@1.6.13':
+    resolution: {integrity: sha512-lTvlSHYDZyWJnAR1W8DOQLwUo32VpRopbS/BPQqStcOV6FqaC+u5YjT50KbJ+oBWcorpzfknhICRwEA3Xm8t9Q==}
+
+  '@antv/g-dom-mutation-observer-api@2.0.38':
+    resolution: {integrity: sha512-xzgbt8GUOiToBeDVv+jmGkDE+HtI9tD6uO8TirJbCya88DKcY/jurQALq0NdWKgMJLn7WPiUKyDwHWimwQcBJw==}
+
+  '@antv/g-lite@2.3.2':
+    resolution: {integrity: sha512-fkIxRoqLOGsNPwsp26bPp58cPWuX3E4wQ9cfkB/DHy5LtLrPpvOwHWB3+MBPgZwzk8jTTjchiXa756ZFOAWyQQ==}
+
+  '@antv/g-math@3.0.1':
+    resolution: {integrity: sha512-FvkDBNRpj+HsLINunrL2PW0OlG368MlpHuihbxleuajGim5kra8tgISwCLmAf8Yz2b1CgZ9PvpohqiLzHS7HLg==}
+
+  '@antv/g-plugin-canvas-path-generator@2.1.22':
+    resolution: {integrity: sha512-Z0IawzTGgTppa9IpkNNKsqgoU89oOjUsiU8GZZlkDkUggQTHP0wOxTeLAb43YgClx3aTI3bRs44uMQutNdSVxw==}
+
+  '@antv/g-plugin-canvas-picker@2.1.27':
+    resolution: {integrity: sha512-DHQ0YLYNXAm6O63pW6nKs/R0fuqlUYfehNs/EtzrmqyUkKASd/Vhs4HLNeHTMUdBMgg41T+x5qay0GGttK4Xdw==}
+
+  '@antv/g-plugin-canvas-renderer@2.3.3':
+    resolution: {integrity: sha512-d6JkZy1YmLnvI9wsbO8QVpBz7z7tl6JRQkF5hx9XLDtf2fD4n83KINeMq13skiNwaiudS771WWiBtfzUHB73pQ==}
+
+  '@antv/g-plugin-device-renderer@2.3.3':
+    resolution: {integrity: sha512-zbigW9baSbtSLSuAVBrXLCiKy+uljq6DttCr/KRdU77DT1nbPBGG/V/RB95UaA4EiEYyLISlKVPFWCYs6Jaszw==}
+
+  '@antv/g-plugin-dom-interaction@2.1.27':
+    resolution: {integrity: sha512-hltVZZH+bj0uXmGSR+6BIwhCFYyHmDIQi3vrj/Wn1Dn6PgufvMCXfjr3DfmkQnY+FFP8ZCpg5N9MaE0BE9OddA==}
+
+  '@antv/g-plugin-dragndrop@2.0.38':
+    resolution: {integrity: sha512-yCef5ER759i0WpuOekFQ+AcDTu0N/COMbkPOG6YuswVnhQH447GUpuNm7Le+Mq26qONlXTDyjxuMHoUOWwJ7Cw==}
+
+  '@antv/g-plugin-html-renderer@2.1.27':
+    resolution: {integrity: sha512-NnI4GxDBb71o/XZzoRdi0xI3xg7GJmthyO5xP5/MiOFmwJ/jW/QDz17vUonmzUVbCt6upikHV5GyYOaogRqdVg==}
+
+  '@antv/g-plugin-image-loader@2.1.26':
+    resolution: {integrity: sha512-AElV0QOX2LAhB3jr9XtvkynntuKhcaU5n7avu5ynM5VoAtMaJRANhCyefA2G3myeJxWcHk4nWDX6u4YMaZnnvw==}
+
+  '@antv/g-shader-components@2.0.0':
+    resolution: {integrity: sha512-qdUAz0ZZSrAFrBKxne7kUMWt2v+kncnpWwdDD7SGa7OCtcnWADl6lUUiL0r29gBMcoDtPXJijsjS/e/w9cVWuQ==}
+
+  '@antv/g-web-animations-api@2.1.28':
+    resolution: {integrity: sha512-V5g8bO2D1hb8fRMMi5hXL/De+1UDRzW3C5EX07oazR0q71GONASP+sVwniZdt9R1HAmJSN5dvW3SqWeU3EEstQ==}
+
+  '@antv/g-webgl@2.0.52':
+    resolution: {integrity: sha512-c/o9U3jyZIXhL8q6MA0EkGL0crwVjMYDvv2hc30rJlyP09uXGyeVfeAQYPa6iaF99VH2u9NT/nX09Y2qYMMGhA==}
+
+  '@antv/g2@5.3.5':
+    resolution: {integrity: sha512-Q2TPfWTf2fMwe02Uqk4Uvgy/RGXpGQXfSMPHKLkwTUh6WrFImpgzU4mHhUewKVu5ppPGGfvlwIbZcHeBnNb5ug==}
+
+  '@antv/g@6.1.28':
+    resolution: {integrity: sha512-BwavpbKGR4NEJD3BtVxfBFjCcxy5gsWoUNnBisfG1qfjhGTt7QvUYHFH46+mHJjHMIdYjuFw2T0ZYVtxBddxSg==}
+
+  '@antv/scale@0.4.16':
+    resolution: {integrity: sha512-5wg/zB5kXHxpTV5OYwJD3ja6R8yTiqIOkjOhmpEJiowkzRlbEC/BOyMvNUq5fqFIHnMCE9woO7+c3zxEQCKPjw==}
+
+  '@antv/util@2.0.17':
+    resolution: {integrity: sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==}
+
+  '@antv/util@3.3.11':
+    resolution: {integrity: sha512-FII08DFM4ABh2q5rPYdr0hMtKXRgeZazvXaFYCs7J7uTcWDHUhczab2qOCJLNDugoj8jFag1djb7wS9ehaRYBg==}
+
+  '@antv/vendor@1.0.11':
+    resolution: {integrity: sha512-LmhPEQ+aapk3barntaiIxJ5VHno/Tyab2JnfdcPzp5xONh/8VSfed4bo/9xKo5HcUAEydko38vYLfj6lJliLiw==}
+
+  '@asamuzakjp/css-color@3.2.0':
+    resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
+
+  '@babel/code-frame@7.27.1':
+    resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.28.0':
+    resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.28.0':
+    resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.28.0':
+    resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-annotate-as-pure@7.27.3':
+    resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.27.2':
+    resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-create-class-features-plugin@7.27.1':
+    resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-globals@7.28.0':
+    resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-member-expression-to-functions@7.27.1':
+    resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.27.1':
+    resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.27.3':
+    resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-optimise-call-expression@7.27.1':
+    resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-plugin-utils@7.27.1':
+    resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-replace-supers@7.27.1':
+    resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+    resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-string-parser@7.27.1':
+    resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.27.1':
+    resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.27.1':
+    resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.28.2':
+    resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.28.0':
+    resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-proposal-decorators@7.28.0':
+    resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-decorators@7.27.1':
+    resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-attributes@7.27.1':
+    resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-meta@7.10.4':
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-jsx@7.27.1':
+    resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-typescript@7.27.1':
+    resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typescript@7.28.0':
+    resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/runtime@7.28.2':
+    resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/template@7.27.2':
+    resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.28.0':
+    resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.28.2':
+    resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@bufbuild/protobuf@2.6.3':
+    resolution: {integrity: sha512-w/gJKME9mYN7ZoUAmSMAWXk4hkVpxRKvEJCb3dV5g9wwWdxTJJ0ayOJAVcNxtdqaxDyFuC0uz4RSGVacJ030PQ==}
+
+  '@clack/core@0.5.0':
+    resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==}
+
+  '@clack/prompts@0.11.0':
+    resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==}
+
+  '@css-render/plugin-bem@0.15.14':
+    resolution: {integrity: sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==}
+    peerDependencies:
+      css-render: ~0.15.14
+
+  '@css-render/vue3-ssr@0.15.14':
+    resolution: {integrity: sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==}
+    peerDependencies:
+      vue: ^3.0.11
+
+  '@csstools/color-helpers@5.0.2':
+    resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
+    engines: {node: '>=18'}
+
+  '@csstools/css-calc@2.1.4':
+    resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
+    engines: {node: '>=18'}
+    peerDependencies:
+      '@csstools/css-parser-algorithms': ^3.0.5
+      '@csstools/css-tokenizer': ^3.0.4
+
+  '@csstools/css-color-parser@3.0.10':
+    resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==}
+    engines: {node: '>=18'}
+    peerDependencies:
+      '@csstools/css-parser-algorithms': ^3.0.5
+      '@csstools/css-tokenizer': ^3.0.4
+
+  '@csstools/css-parser-algorithms@3.0.5':
+    resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
+    engines: {node: '>=18'}
+    peerDependencies:
+      '@csstools/css-tokenizer': ^3.0.4
+
+  '@csstools/css-tokenizer@3.0.4':
+    resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
+    engines: {node: '>=18'}
+
+  '@dotenvx/dotenvx@1.48.4':
+    resolution: {integrity: sha512-GpJWpGVI5JGhNzFlWOjCD3KMiN3xU1US4oLKQ7SiiGru4LvR7sUf3pDMpfjtlgzHStL5ydq4ekfZcRxWpHaJkA==}
+    hasBin: true
+
+  '@ecies/ciphers@0.2.4':
+    resolution: {integrity: sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==}
+    engines: {bun: '>=1', deno: '>=2', node: '>=16'}
+    peerDependencies:
+      '@noble/ciphers': ^1.0.0
+
+  '@emnapi/core@1.4.5':
+    resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==}
+
+  '@emnapi/runtime@1.4.5':
+    resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==}
+
+  '@emnapi/wasi-threads@1.0.4':
+    resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==}
+
+  '@emotion/hash@0.8.0':
+    resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
+
+  '@es-joy/jsdoccomment@0.50.2':
+    resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==}
+    engines: {node: '>=18'}
+
+  '@es-joy/jsdoccomment@0.52.0':
+    resolution: {integrity: sha512-BXuN7BII+8AyNtn57euU2Yxo9yA/KUDNzrpXyi3pfqKmBhhysR6ZWOebFh3vyPoqA3/j1SOvGgucElMGwlXing==}
+    engines: {node: '>=20.11.0'}
+
+  '@esbuild/aix-ppc64@0.25.8':
+    resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.25.8':
+    resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.25.8':
+    resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.25.8':
+    resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.25.8':
+    resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.25.8':
+    resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.25.8':
+    resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.25.8':
+    resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.25.8':
+    resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.25.8':
+    resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.25.8':
+    resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.25.8':
+    resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.25.8':
+    resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.25.8':
+    resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.25.8':
+    resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.25.8':
+    resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.25.8':
+    resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-arm64@0.25.8':
+    resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.25.8':
+    resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-arm64@0.25.8':
+    resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.25.8':
+    resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/openharmony-arm64@0.25.8':
+    resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@esbuild/sunos-x64@0.25.8':
+    resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.25.8':
+    resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.25.8':
+    resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.25.8':
+    resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
+  '@eslint-community/eslint-plugin-eslint-comments@4.5.0':
+    resolution: {integrity: sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+  '@eslint-community/eslint-utils@4.7.0':
+    resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+  '@eslint-community/regexpp@4.12.1':
+    resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  '@eslint/compat@1.3.1':
+    resolution: {integrity: sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.40 || 9
+    peerDependenciesMeta:
+      eslint:
+        optional: true
+
+  '@eslint/config-array@0.21.0':
+    resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/config-helpers@0.3.0':
+    resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/core@0.15.1':
+    resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/eslintrc@3.3.1':
+    resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/js@9.32.0':
+    resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/markdown@7.1.0':
+    resolution: {integrity: sha512-Y+X1B1j+/zupKDVJfkKc8uYMjQkGzfnd8lt7vK3y8x9Br6H5dBuhAfFrQ6ff7HAMm/1BwgecyEiRFkYCWPRxmA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/object-schema@2.1.6':
+    resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/plugin-kit@0.3.4':
+    resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@humanfs/core@0.19.1':
+    resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanfs/node@0.16.6':
+    resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanwhocodes/module-importer@1.0.1':
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+
+  '@humanwhocodes/retry@0.3.1':
+    resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
+    engines: {node: '>=18.18'}
+
+  '@humanwhocodes/retry@0.4.3':
+    resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+    engines: {node: '>=18.18'}
+
+  '@iconify-json/carbon@1.2.13':
+    resolution: {integrity: sha512-vXrFFKkfYaZSwf4WfnRQJQMnpEzxlPlupwZZlS1WvOAuNnWahsUudHycaMpoBzDqBfniH4KQ2b5qnw0VMKoJug==}
+
+  '@iconify-json/fluent@1.2.28':
+    resolution: {integrity: sha512-Lnaf38ruWopJv8PdEsK8vXgxKpFSsKED2kI0/575ZXg6XHUMvzSuLlldAVAOEWZmMVPJFC0MXe07Ybnh1ziLvw==}
+
+  '@iconify-json/hugeicons@1.2.6':
+    resolution: {integrity: sha512-Sv+Oh7db6mAfgG9M/I5Do+eL8+978zuzxPEK8ogtX7wWVvYd0TfrWRESdNBf4COS4oav8pGaZDWr4tBMy+Qemg==}
+
+  '@iconify-json/mage@1.2.2':
+    resolution: {integrity: sha512-sCn5NRf4MrKjGzElJ7LtZHCNcyTEc/+szCAgpuOujaL7Qhht+cMNwmKXJmlcCcezxiOmL0TDjwdzQZ5xkSkJhw==}
+
+  '@iconify-json/material-symbols@1.2.30':
+    resolution: {integrity: sha512-3dA1jxkCd8LqEaAWjwQ5tob4pJNKE59Bz604AL1GP3Kyv//pR3un3cIyAUWOFQS1lz0vKKkn0BKOCmcUDi1G8A==}
+
+  '@iconify-json/mdi@1.2.3':
+    resolution: {integrity: sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==}
+
+  '@iconify-json/ph@1.2.2':
+    resolution: {integrity: sha512-PgkEZNtqa8hBGjHXQa4pMwZa93hmfu8FUSjs/nv4oUU6yLsgv+gh9nu28Kqi8Fz9CCVu4hj1MZs9/60J57IzFw==}
+
+  '@iconify-json/pixelarticons@1.2.2':
+    resolution: {integrity: sha512-BAAzqfT0XKUc/a8MgTxOH1aSm44InW9abUIQ1qu5dBntKlYuJP/dN0JfK4WEpUS78T/7HP8w9v1nSQjENLyjFg==}
+
+  '@iconify-json/prime@1.2.2':
+    resolution: {integrity: sha512-W08l1i445VOmiOounOAzHHcpW3i3B+EkRJlkMSCIlAmZNnrdCE+JRUG4GhEA5WlIYu/rqIDmpAF+0YZLH4RNBg==}
+
+  '@iconify-json/streamline-freehand@1.2.1':
+    resolution: {integrity: sha512-3Pq1cEsAtSkilrbCM9KzcVFuET+XD/WhSOOEpOzD2nUq8t3D8XB/klBQ2emu6SYv7FUnszhTrfdinYWaDFQbKw==}
+
+  '@iconify/types@2.0.0':
+    resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+  '@iconify/utils@2.3.0':
+    resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==}
+
+  '@isaacs/balanced-match@4.0.1':
+    resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
+    engines: {node: 20 || >=22}
+
+  '@isaacs/brace-expansion@5.0.0':
+    resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
+    engines: {node: 20 || >=22}
+
+  '@isaacs/cliui@8.0.2':
+    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+    engines: {node: '>=12'}
+
+  '@jridgewell/gen-mapping@0.3.12':
+    resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/source-map@0.3.10':
+    resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==}
+
+  '@jridgewell/sourcemap-codec@1.5.4':
+    resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
+
+  '@jridgewell/trace-mapping@0.3.29':
+    resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+
+  '@juggle/resize-observer@3.4.0':
+    resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
+
+  '@napi-rs/wasm-runtime@1.0.1':
+    resolution: {integrity: sha512-KVlQ/jgywZpixGCKMNwxStmmbYEMyokZpCf2YuIChhfJA2uqfAKNEM8INz7zzTo55iEXfBhIIs3VqYyqzDLj8g==}
+
+  '@noble/ciphers@1.3.0':
+    resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==}
+    engines: {node: ^14.21.3 || >=16}
+
+  '@noble/curves@1.9.6':
+    resolution: {integrity: sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==}
+    engines: {node: ^14.21.3 || >=16}
+
+  '@noble/hashes@1.8.0':
+    resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
+    engines: {node: ^14.21.3 || >=16}
+
+  '@nodelib/fs.scandir@2.1.5':
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.stat@2.0.5':
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.walk@1.2.8':
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+
+  '@one-ini/wasm@0.1.1':
+    resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+
+  '@oxc-project/runtime@0.80.0':
+    resolution: {integrity: sha512-3rzy1bJAZ4s7zV9TKT60x119RwJDCDqEtCwK/Zc2qlm7wGhiIUxLLYUhE/mN91yB0u1kxm5sh4NjU12sPqQTpg==}
+    engines: {node: '>=6.9.0'}
+
+  '@oxc-project/types@0.80.0':
+    resolution: {integrity: sha512-xxHQm8wfCv2e8EmtaDwpMeAHOWqgQDAYg+BJouLXSQt5oTKu9TIXrgNMGSrM2fLvKmECsRd9uUFAAD+hPyootA==}
+
+  '@oxc-resolver/binding-android-arm-eabi@11.6.1':
+    resolution: {integrity: sha512-Ma/kg29QJX1Jzelv0Q/j2iFuUad1WnjgPjpThvjqPjpOyLjCUaiFCCnshhmWjyS51Ki1Iol3fjf1qAzObf8GIA==}
+    cpu: [arm]
+    os: [android]
+
+  '@oxc-resolver/binding-android-arm64@11.6.1':
+    resolution: {integrity: sha512-xjL/FKKc5p8JkFWiH7pJWSzsewif3fRf1rw2qiRxRvq1uIa6l7Zoa14Zq2TNWEsqDjdeOrlJtfWiPNRnevK0oQ==}
+    cpu: [arm64]
+    os: [android]
+
+  '@oxc-resolver/binding-darwin-arm64@11.6.1':
+    resolution: {integrity: sha512-u0yrJ3NHE0zyCjiYpIyz4Vmov21MA0yFKbhHgixDU/G6R6nvC8ZpuSFql3+7C8ttAK9p8WpqOGweepfcilH5Bw==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@oxc-resolver/binding-darwin-x64@11.6.1':
+    resolution: {integrity: sha512-2lox165h1EhzxcC8edUy0znXC/hnAbUPaMpYKVlzLpB2AoYmgU4/pmofFApj+axm2FXpNamjcppld8EoHo06rw==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@oxc-resolver/binding-freebsd-x64@11.6.1':
+    resolution: {integrity: sha512-F45MhEQ7QbHfsvZtVNuA/9obu3il7QhpXYmCMfxn7Zt9nfAOw4pQ8hlS5DroHVp3rW35u9F7x0sixk/QEAi3qQ==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@oxc-resolver/binding-linux-arm-gnueabihf@11.6.1':
+    resolution: {integrity: sha512-r+3+MTTl0tD4NoWbfTIItAxJvuyIU7V0fwPDXrv7Uj64vZ3OYaiyV+lVaeU89Bk/FUUQxeUpWBwdKNKHjyRNQw==}
+    cpu: [arm]
+    os: [linux]
+
+  '@oxc-resolver/binding-linux-arm-musleabihf@11.6.1':
+    resolution: {integrity: sha512-TBTZ63otsWZ72Z8ZNK2JVS0HW1w9zgOixJTFDNrYPUUW1pXGa28KAjQ1yGawj242WLAdu3lwdNIWtkxeO2BLxQ==}
+    cpu: [arm]
+    os: [linux]
+
+  '@oxc-resolver/binding-linux-arm64-gnu@11.6.1':
+    resolution: {integrity: sha512-SjwhNynjSG2yMdyA0f7wz7Yvo3ppejO+ET7n2oiI7ApCXrwxMzeRWjBzQt+oVWr2HzVOfaEcDS9rMtnR83ulig==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@oxc-resolver/binding-linux-arm64-musl@11.6.1':
+    resolution: {integrity: sha512-f4EMidK6rosInBzPMnJ0Ri4RttFCvvLNUNDFUBtELW/MFkBwPTDlvbsmW0u0Mk/ruBQ2WmRfOZ6tT62kWMcX2Q==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@oxc-resolver/binding-linux-ppc64-gnu@11.6.1':
+    resolution: {integrity: sha512-1umENVKeUsrWnf5IlF/6SM7DCv8G6CoKI2LnYR6qhZuLYDPS4PBZ0Jow3UDV9Rtbv5KRPcA3/uXjI88ntWIcOQ==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@oxc-resolver/binding-linux-riscv64-gnu@11.6.1':
+    resolution: {integrity: sha512-Hjyp1FRdJhsEpIxsZq5VcDuFc8abC0Bgy8DWEa31trCKoTz7JqA7x3E2dkFbrAKsEFmZZ0NvuG5Ip3oIRARhow==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@oxc-resolver/binding-linux-riscv64-musl@11.6.1':
+    resolution: {integrity: sha512-ODJOJng6f3QxpAXhLel3kyWs8rPsJeo9XIZHzA7p//e+5kLMDU7bTVk4eZnUHuxsqsB8MEvPCicJkKCEuur5Ag==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [musl]
+
+  '@oxc-resolver/binding-linux-s390x-gnu@11.6.1':
+    resolution: {integrity: sha512-hCzRiLhqe1ZOpHTsTGKp7gnMJRORlbCthawBueer2u22RVAka74pV/+4pP1tqM07mSlQn7VATuWaDw9gCl+cVg==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@oxc-resolver/binding-linux-x64-gnu@11.6.1':
+    resolution: {integrity: sha512-JansPD8ftOzMYIC3NfXJ68tt63LEcIAx44Blx6BAd7eY880KX7A0KN3hluCrelCz5aQkPaD95g8HBiJmKaEi2w==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@oxc-resolver/binding-linux-x64-musl@11.6.1':
+    resolution: {integrity: sha512-R78ES1rd4z2x5NrFPtSWb/ViR1B8wdl+QN2X8DdtoYcqZE/4tvWtn9ZTCXMEzUp23tchJ2wUB+p6hXoonkyLpA==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@oxc-resolver/binding-wasm32-wasi@11.6.1':
+    resolution: {integrity: sha512-qAR3tYIf3afkij/XYunZtlz3OH2Y4ni10etmCFIJB5VRGsqJyI6Hl+2dXHHGJNwbwjXjSEH/KWJBpVroF3TxBw==}
+    engines: {node: '>=14.0.0'}
+    cpu: [wasm32]
+
+  '@oxc-resolver/binding-win32-arm64-msvc@11.6.1':
+    resolution: {integrity: sha512-QqygWygIuemGkaBA48POOTeinbVvlamqh6ucm8arGDGz/mB5O00gXWxed12/uVrYEjeqbMkla/CuL3fjL3EKvw==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@oxc-resolver/binding-win32-ia32-msvc@11.6.1':
+    resolution: {integrity: sha512-N2+kkWwt/bk0JTCxhPuK8t8JMp3nd0n2OhwOkU8KO4a7roAJEa4K1SZVjMv5CqUIr5sx2CxtXRBoFDiORX5oBg==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@oxc-resolver/binding-win32-x64-msvc@11.6.1':
+    resolution: {integrity: sha512-DfMg3cU9bJUbN62Prbp4fGCtLgexuwyEaQGtZAp8xmi1Ii26uflOGx0FJkFTF6lVMSFoIRFvIL8gsw5/ZdHrMw==}
+    cpu: [x64]
+    os: [win32]
+
+  '@parcel/watcher-android-arm64@2.5.1':
+    resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [android]
+
+  '@parcel/watcher-darwin-arm64@2.5.1':
+    resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@parcel/watcher-darwin-x64@2.5.1':
+    resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@parcel/watcher-freebsd-x64@2.5.1':
+    resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@parcel/watcher-linux-arm-glibc@2.5.1':
+    resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-arm-musl@2.5.1':
+    resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.1':
+    resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-arm64-musl@2.5.1':
+    resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-linux-x64-glibc@2.5.1':
+    resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@parcel/watcher-linux-x64-musl@2.5.1':
+    resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@parcel/watcher-win32-arm64@2.5.1':
+    resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@parcel/watcher-win32-ia32@2.5.1':
+    resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@parcel/watcher-win32-x64@2.5.1':
+    resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [win32]
+
+  '@parcel/watcher@2.5.1':
+    resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+    engines: {node: '>= 10.0.0'}
+
+  '@pkgjs/parseargs@0.11.0':
+    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+    engines: {node: '>=14'}
+
+  '@pkgr/core@0.2.9':
+    resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
+    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+  '@playwright/test@1.54.2':
+    resolution: {integrity: sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  '@polka/url@1.0.0-next.29':
+    resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
+  '@publint/pack@0.1.2':
+    resolution: {integrity: sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==}
+    engines: {node: '>=18'}
+
+  '@quansync/fs@0.1.3':
+    resolution: {integrity: sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg==}
+    engines: {node: '>=20.0.0'}
+
+  '@rolldown/binding-android-arm64@1.0.0-beta.31':
+    resolution: {integrity: sha512-0mFtKwOG7smn0HkvQ6h8j0m/ohkR7Fp5eMTJ2Pns/HSbePHuDpxMaQ4TjZ6arlVXxpeWZlAHeT5BeNsOA3iWTg==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rolldown/binding-darwin-arm64@1.0.0-beta.31':
+    resolution: {integrity: sha512-BHfHJ8Nb5G7ZKJl6pimJacupONT4F7w6gmQHw41rouAnJF51ORDwGefWeb6OMLzGmJwzxlIVPERfnJf1EsMM7A==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rolldown/binding-darwin-x64@1.0.0-beta.31':
+    resolution: {integrity: sha512-4MiuRtExC08jHbSU/diIL+IuQP+3Ck1FbWAplK+ysQJ7fxT3DMxy5FmnIGfmhaqow8oTjb2GEwZJKgTRjZL1Vw==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rolldown/binding-freebsd-x64@1.0.0-beta.31':
+    resolution: {integrity: sha512-nffC1u7ccm12qlAea8ExY3AvqlaHy/o/3L4p5Es8JFJ3zJSs6e3DyuxGZZVdl9EVwsLxPPTvioIl4tEm2afwyw==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.31':
+    resolution: {integrity: sha512-LHmAaB3rB1GOJuHscKcL2Ts/LKLcb3YWTh2uQ/876rg/J9WE9kQ0kZ+3lRSYbth/YL8ln54j4JZmHpqQY3xptQ==}
+    cpu: [arm]
+    os: [linux]
+
+  '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.31':
+    resolution: {integrity: sha512-oTDZVfqIAjLB2I1yTiLyyhfPPO6dky33sTblxTCpe+ZT55WizN3KDoBKJ4yXG8shI6I4bRShVu29Xg0yAjyQYw==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rolldown/binding-linux-arm64-musl@1.0.0-beta.31':
+    resolution: {integrity: sha512-duJ3IkEBj9Xe9NYW1n8Y3483VXHGi8zQ0ZsLbK8464EJUXLF7CXM8Ry+jkkUw+ZvA+Zu1E/+C6p2Y6T9el0C9g==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rolldown/binding-linux-arm64-ohos@1.0.0-beta.31':
+    resolution: {integrity: sha512-qdbmU5QSZ0uoLZBYMxiHsMQmizqtzFGTVPU5oyU1n0jU0Mo+mkSzqZuL8VBnjHOHzhVxZsoAGH9JjiRzCnoGVA==}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@rolldown/binding-linux-x64-gnu@1.0.0-beta.31':
+    resolution: {integrity: sha512-H7+r34TSV8udB2gAsebFM/YuEeNCkPGEAGJ1JE7SgI9XML6FflqcdKfrRSneQFsPaom/gCEc1g0WW5MZ0O3blw==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rolldown/binding-linux-x64-musl@1.0.0-beta.31':
+    resolution: {integrity: sha512-zRm2YmzFVqbsmUsyyZnHfJrOlQUcWS/FJ5ZWL8Q1kZh5PnLBrTVZNpakIWwAxpN5gNEi9MmFd5YHocVJp8ps1Q==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rolldown/binding-wasm32-wasi@1.0.0-beta.31':
+    resolution: {integrity: sha512-fM1eUIuHLsNJXRlWOuIIex1oBJ89I0skFWo5r/D3KSJ5gD9MBd3g4Hp+v1JGohvyFE+7ylnwRxSUyMEeYpA69A==}
+    engines: {node: '>=14.0.0'}
+    cpu: [wasm32]
+
+  '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.31':
+    resolution: {integrity: sha512-4nftR9V2KHH3zjBwf6leuZZJQZ7v0d70ogjHIqB3SDsbDLvVEZiGSsSn2X6blSZRZeJSFzK0pp4kZ67zdZXwSw==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.31':
+    resolution: {integrity: sha512-0TQcKu9xZVHYALit+WJsSuADGlTFfOXhnZoIHWWQhTk3OgbwwbYcSoZUXjRdFmR6Wswn4csHtJGN1oYKeQ6/2g==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rolldown/binding-win32-x64-msvc@1.0.0-beta.31':
+    resolution: {integrity: sha512-3zMICWwpZh1jrkkKDYIUCx/2wY3PXLICAS0AnbeLlhzfWPhCcpNK9eKhiTlLAZyTp+3kyipoi/ZSVIh+WDnBpQ==}
+    cpu: [x64]
+    os: [win32]
+
+  '@rolldown/pluginutils@1.0.0-beta.29':
+    resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
+
+  '@rolldown/pluginutils@1.0.0-beta.31':
+    resolution: {integrity: sha512-IaDZ9NhjOIOkYtm+hH0GX33h3iVZ2OeSUnFF0+7Z4+1GuKs4Kj5wK3+I2zNV9IPLfqV4XlwWif8SXrZNutxciQ==}
+
+  '@rollup/pluginutils@4.2.1':
+    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+    engines: {node: '>= 8.0.0'}
+
+  '@rollup/rollup-android-arm-eabi@4.46.2':
+    resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.46.2':
+    resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.46.2':
+    resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.46.2':
+    resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-freebsd-arm64@4.46.2':
+    resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/rollup-freebsd-x64@4.46.2':
+    resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
+    resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.46.2':
+    resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-arm64-gnu@4.46.2':
+    resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm64-musl@4.46.2':
+    resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
+    resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-ppc64-gnu@4.46.2':
+    resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.46.2':
+    resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-musl@4.46.2':
+    resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-s390x-gnu@4.46.2':
+    resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-gnu@4.46.2':
+    resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-musl@4.46.2':
+    resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-win32-arm64-msvc@4.46.2':
+    resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.46.2':
+    resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.46.2':
+    resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==}
+    cpu: [x64]
+    os: [win32]
+
+  '@sec-ant/readable-stream@0.4.1':
+    resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
+
+  '@sindresorhus/merge-streams@4.0.0':
+    resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
+    engines: {node: '>=18'}
+
+  '@stylistic/eslint-plugin@5.2.2':
+    resolution: {integrity: sha512-bE2DUjruqXlHYP3Q2Gpqiuj2bHq7/88FnuaS0FjeGGLCy+X6a07bGVuwtiOYnPSLHR6jmx5Bwdv+j7l8H+G97A==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: '>=9.0.0'
+
+  '@ts-morph/common@0.27.0':
+    resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==}
+
+  '@tsconfig/node22@22.0.2':
+    resolution: {integrity: sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==}
+
+  '@tybys/wasm-util@0.10.0':
+    resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==}
+
+  '@types/chai@5.2.2':
+    resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
+
+  '@types/chroma-js@3.1.1':
+    resolution: {integrity: sha512-SFCr4edNkZ1bGaLzGz7rgR1bRzVX4MmMxwsIa3/Bh6ose8v+hRpneoizHv0KChdjxaXyjRtaMq7sCuZSzPomQA==}
+
+  '@types/d3-array@3.2.1':
+    resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
+
+  '@types/d3-color@3.1.3':
+    resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+  '@types/d3-dispatch@3.0.7':
+    resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==}
+
+  '@types/d3-dsv@3.0.7':
+    resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
+
+  '@types/d3-ease@3.0.2':
+    resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+  '@types/d3-fetch@3.0.7':
+    resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
+
+  '@types/d3-force@3.0.10':
+    resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
+
+  '@types/d3-format@3.0.4':
+    resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
+
+  '@types/d3-geo@3.1.0':
+    resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
+
+  '@types/d3-hierarchy@3.1.7':
+    resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
+
+  '@types/d3-interpolate@3.0.4':
+    resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+  '@types/d3-path@3.1.1':
+    resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+  '@types/d3-quadtree@3.0.6':
+    resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
+
+  '@types/d3-random@3.0.3':
+    resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
+
+  '@types/d3-scale-chromatic@3.1.0':
+    resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==}
+
+  '@types/d3-scale@4.0.9':
+    resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+  '@types/d3-shape@3.1.7':
+    resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==}
+
+  '@types/d3-time@3.0.4':
+    resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+  '@types/d3-timer@3.0.2':
+    resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
+  '@types/debug@4.1.12':
+    resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+
+  '@types/deep-eql@4.0.2':
+    resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
+  '@types/estree@1.0.8':
+    resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+  '@types/geojson@7946.0.16':
+    resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
+
+  '@types/jsdom@21.1.7':
+    resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==}
+
+  '@types/json-schema@7.0.15':
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+  '@types/katex@0.16.7':
+    resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
+
+  '@types/lodash-es@4.17.12':
+    resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+  '@types/lodash@4.17.20':
+    resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==}
+
+  '@types/mdast@4.0.4':
+    resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+  '@types/ms@2.1.0':
+    resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+  '@types/node@22.17.0':
+    resolution: {integrity: sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==}
+
+  '@types/sortablejs@1.15.8':
+    resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
+
+  '@types/tough-cookie@4.0.5':
+    resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
+
+  '@types/unist@3.0.3':
+    resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+  '@types/web-bluetooth@0.0.21':
+    resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+  '@typescript-eslint/eslint-plugin@8.39.0':
+    resolution: {integrity: sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^8.39.0
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <6.0.0'
+
+  '@typescript-eslint/parser@8.39.0':
+    resolution: {integrity: sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <6.0.0'
+
+  '@typescript-eslint/project-service@8.39.0':
+    resolution: {integrity: sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <6.0.0'
+
+  '@typescript-eslint/scope-manager@8.39.0':
+    resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/tsconfig-utils@8.39.0':
+    resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <6.0.0'
+
+  '@typescript-eslint/type-utils@8.39.0':
+    resolution: {integrity: sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <6.0.0'
+
+  '@typescript-eslint/types@8.39.0':
+    resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/typescript-estree@8.39.0':
+    resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <6.0.0'
+
+  '@typescript-eslint/utils@8.39.0':
+    resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      typescript: '>=4.8.4 <6.0.0'
+
+  '@typescript-eslint/visitor-keys@8.39.0':
+    resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript/native-preview-darwin-arm64@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-sZAKCg5lW10rg4REkm6Qv14+f2OVN4dX+AfznnxZp3iqj9am+0JCg67la3IVlhznY3jwvqJU6QK54vblK+my5A==}
+    engines: {node: '>=20.6.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@typescript/native-preview-darwin-x64@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-EkcJWqnldvsEM59hNicAFGp0a3y4ebp60uJdlgxuOC3kFHffMHTUSxhuxySY4djKV9uei5RZR2O5s3XDrjcKoA==}
+    engines: {node: '>=20.6.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@typescript/native-preview-linux-arm64@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-nG07i0oVCISBVPqoHKPpBwYUg3XJhuo/QOScvTagJ4o1XS63HCZOldC0cu26ZyvlrgBaWmteVMzyaaIluwupNQ==}
+    engines: {node: '>=20.6.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@typescript/native-preview-linux-arm@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-yhfOqmxyscXYd6gkrvmDInVx5fntetIrIBHGw1ElNmnmlu5HgguXfEXLxYNMVnBRTzrHtHYPQ8jRd79X1uni+g==}
+    engines: {node: '>=20.6.0'}
+    cpu: [arm]
+    os: [linux]
+
+  '@typescript/native-preview-linux-x64@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-HrVUxN+VBDiiyi3xk4Ih323ZXAyLWu4CNlI1MGqazhOvahKUjTTvuyzdyTWI7Bzf25KcHIXqfyD0PTqQQRMnVw==}
+    engines: {node: '>=20.6.0'}
+    cpu: [x64]
+    os: [linux]
+
+  '@typescript/native-preview-win32-arm64@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-TRSeuNIA3CI8BmZhioj+QBFdsywadJWKvdC/dhawaYJJuhwz4jbPD7lg/SA2gWihqDC7ZhPq6eqSgaxobW5/uw==}
+    engines: {node: '>=20.6.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@typescript/native-preview-win32-x64@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-LTLw0FtGmrSZ3mrERcDWJ1P7tvGMZAi5dhbwXBhGHiawX8kwWHZI3J+TqDnU2zrxL22164qsAee2LI4J2bwpNw==}
+    engines: {node: '>=20.6.0'}
+    cpu: [x64]
+    os: [win32]
+
+  '@typescript/native-preview@7.0.0-dev.20250819.1':
+    resolution: {integrity: sha512-+gl0Sg+Ol5QibPacZziwHyImPtPzuYV1BF/n99LG6U9BAMOXoBamU+9Y5C+0gUdesmz5Ik9ZJpUuX4HTBzULig==}
+    engines: {node: '>=20.6.0'}
+    hasBin: true
+
+  '@umami/node@0.4.0':
+    resolution: {integrity: sha512-pyphprbiF7KiDSc+SWZ4/rVM8B5vU27zIiFfEPj2lEqczpI4xAKSp+dM3tlzyRAWJL32fcbCfAaLGhJZQV13Rg==}
+
+  '@unocss/astro@66.4.1':
+    resolution: {integrity: sha512-UPfpg1FGf8lMugnMYuFxpVqNvO9cIVdi7e3yXpBnzf0uQ4Pib7KvOTPB4VOAVoqDK2A0kPxCzeR9MxCk4c+wxw==}
+    peerDependencies:
+      vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+    peerDependenciesMeta:
+      vite:
+        optional: true
+
+  '@unocss/cli@66.4.1':
+    resolution: {integrity: sha512-rf6udln8CY70tRP2BU1zorJZjzhDVIO3JzmxvH+q4W7MZBQ2ZRfZkWKUFa6ZiyjIj8lxEWlmT6zvu3aSJXbHTg==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  '@unocss/config@66.4.1':
+    resolution: {integrity: sha512-ABiYEN9febHAfvpxzjrqdCrkomcDpcjCL1OPrWXLHOecfi2qZkj5sTASAfGUvqrV2WtZTPDQii6Nz1OisitRQQ==}
+    engines: {node: '>=14'}
+
+  '@unocss/core@66.4.1':
+    resolution: {integrity: sha512-2QMthvEmH0215zqIIL5WCodQ9lYShYDzziF4N1MMdcoRvCumGq9MMBFPxC54gNDrO3+/Olhp++YYmXkdZ89qrA==}
+
+  '@unocss/extractor-arbitrary-variants@66.4.1':
+    resolution: {integrity: sha512-1iG2egoarrpht56AMoccUMc5iEMPLOQY0iwPumGtwTFqFAYntAQLHbFL//BCOD7DRrHMvGLVNt+uKEGXYIvW6w==}
+
+  '@unocss/inspector@66.4.1':
+    resolution: {integrity: sha512-QS2zOaJaddDFQ7g3BRlFnxqyPYEa5XtE6cwo58hgeZwFDak8IAyPBYpFbINfCQzXhguqGO4MyMRX6NXzgKQ1Cw==}
+
+  '@unocss/postcss@66.4.1':
+    resolution: {integrity: sha512-UcpMqOJ+ADn9uGetWgmIV+Ctu6Y/W828bJKGRPn6u+c+kporLWfkV5nwSqo1C3o1ADkChwpzs0Ikpz3emxM7yQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      postcss: ^8.4.21
+
+  '@unocss/preset-attributify@66.4.1':
+    resolution: {integrity: sha512-HTJd/4bEie6EsCwqq9hK+GJwoFp0rx6k3GTz9jHXMwhGpUGYPrrccw8cazYmJq7lMHnPbh/spxHH24eTISwojg==}
+
+  '@unocss/preset-icons@66.4.1':
+    resolution: {integrity: sha512-2XONpwvWcedBRmGangrTcHKGcXN46tZicZIVBa9QCDZ+VsAzOdHzvHWl4QinoYnBhCs6yE+sxZ0sETfFwjOsEg==}
+
+  '@unocss/preset-mini@66.4.1':
+    resolution: {integrity: sha512-3yzpUty02AGgZRBlcRhgpEEsG+GJYvweh1TqWql0bAiW3GJWx0oNuvy2jEQjgllvRYRAErIW4/doOljGNQwwjw==}
+
+  '@unocss/preset-tagify@66.4.1':
+    resolution: {integrity: sha512-PGlC8NwojPc1mj5cvYq5JmBaK7wigcElAItGS+uVQW6goC5kyeiiB0vNw1iyfG542yzXJpPkb3d6B4wZpjl21w==}
+
+  '@unocss/preset-typography@66.4.1':
+    resolution: {integrity: sha512-V7al6aFsmLcj/eyXuvyf2RIlmTpnUJvWnG4A/LZd8UNsLCndFecE0sTR9VYG1JgNj9aywtJT2w6MSSFnN7Jf5g==}
+
+  '@unocss/preset-uno@66.4.1':
+    resolution: {integrity: sha512-iRx0GSSCQFL0rjQs9/rNM+igoBup44kCe+yHcB86/nUaRodQbWU2287xoXZcnzcg7/MTjtEtQWu220EY940pmg==}
+
+  '@unocss/preset-web-fonts@66.4.1':
+    resolution: {integrity: sha512-+wVO+kZTZVREDXtD4/0sJQIBfid+bim40AF4mYp5wA85aeWCCZIqvXzPeF4BWvGFqqrQDHEO1TcUbnOoP8V8UQ==}
+
+  '@unocss/preset-wind3@66.4.1':
+    resolution: {integrity: sha512-GB2evqTAYw3Z9MImIl3JN5T1of8BO40X9zXnpsVQ4w/v/TOUdVzhfsc4XTVOr8/VVO6Z7VNczgpsd6lZhbrcyg==}
+
+  '@unocss/preset-wind4@66.4.1':
+    resolution: {integrity: sha512-mDomSDXAWqnKTbmgEws9WxUxfvGoEzhy/NmpmHRX7R7wm1LAD7FGUl7pF11N51Wf6Z5in8iTzqSFcs0YfUfYng==}
+
+  '@unocss/preset-wind@66.4.1':
+    resolution: {integrity: sha512-r/dUy+0G20/UO6bcZFnNXoRu/6KdRzVdfbeH5TjgO8riI7U44ckLQjDhCMUgUgYldIHewazyiDfMWBcYs5QF5w==}
+
+  '@unocss/reset@66.4.1':
+    resolution: {integrity: sha512-f0t2YIRF7uVUwEB0aDR6yPMqBO+QgGdgACWQmCrZ84Kir9HV+5d50tK5tXDflHmFTu9mqS9MUG4Ba3sPp44vZw==}
+
+  '@unocss/rule-utils@66.4.1':
+    resolution: {integrity: sha512-iYbx4ab3aTMrcJSyBoGvwsbUM5BmRQef4rzGOjL312r/0RDZM+i2ikPxcFXpp/9P+Gp22gSXDMlzwY+VfJ2Llw==}
+    engines: {node: '>=14'}
+
+  '@unocss/transformer-attributify-jsx@66.4.1':
+    resolution: {integrity: sha512-5aICECaco87n1p/8/vzvtgIly3lu1htd/n2SdfA1t6gtUO7PMX2CEZGSw4VxnMTrjNZRXUqxD7MACJIEg/kzRw==}
+
+  '@unocss/transformer-compile-class@66.4.1':
+    resolution: {integrity: sha512-xf6W+zLbFwOvlNNxV0S73ggevlmiBhqtWBI4xdd463IdiJJ1VU/hTmXJ0r39hVHwVEPYGtechUODGtky47M59Q==}
+
+  '@unocss/transformer-directives@66.4.1':
+    resolution: {integrity: sha512-3PiYgq0/TYeCLoUm6K+XbsHcd03l3TN2U2fSeda4PIZoZ/ioH0kJd0tVg98DzO8ekgqtUwKFZjgBa0ezQJwfPQ==}
+
+  '@unocss/transformer-variant-group@66.4.1':
+    resolution: {integrity: sha512-stFDCkPeZYBRpKmmF8dOBgAZEh8ydzSrBnc4Uiakgn0LXrhAKNgxR/kC8urgMhu+HcyNsWlLEEIeQyPj72IAHA==}
+
+  '@unocss/vite@66.4.1':
+    resolution: {integrity: sha512-oXXh9FKaxHUbd9GaiVg+CKtVDoWubQNKtHOLExXU3GgZ/jHATPawR2LXoOtPl/9qq6lT6Dl+o4el2AauUQqo7g==}
+    peerDependencies:
+      vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  '@vitejs/plugin-vue-jsx@5.0.1':
+    resolution: {integrity: sha512-X7qmQMXbdDh+sfHUttXokPD0cjPkMFoae7SgbkF9vi3idGUKmxLcnU2Ug49FHwiKXebfzQRIm5yK3sfCJzNBbg==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+      vue: ^3.0.0
+
+  '@vitejs/plugin-vue@6.0.1':
+    resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+      vue: ^3.2.25
+
+  '@vitest/eslint-plugin@1.3.4':
+    resolution: {integrity: sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==}
+    peerDependencies:
+      eslint: '>= 8.57.0'
+      typescript: '>= 5.0.0'
+      vitest: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+      vitest:
+        optional: true
+
+  '@vitest/expect@3.2.4':
+    resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
+
+  '@vitest/mocker@3.2.4':
+    resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
+    peerDependencies:
+      msw: ^2.4.9
+      vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
+    peerDependenciesMeta:
+      msw:
+        optional: true
+      vite:
+        optional: true
+
+  '@vitest/pretty-format@3.2.4':
+    resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
+
+  '@vitest/runner@3.2.4':
+    resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
+
+  '@vitest/snapshot@3.2.4':
+    resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
+
+  '@vitest/spy@3.2.4':
+    resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
+
+  '@vitest/ui@3.2.4':
+    resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==}
+    peerDependencies:
+      vitest: 3.2.4
+
+  '@vitest/utils@3.2.4':
+    resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
+
+  '@volar/language-core@2.4.22':
+    resolution: {integrity: sha512-gp4M7Di5KgNyIyO903wTClYBavRt6UyFNpc5LWfyZr1lBsTUY+QrVZfmbNF2aCyfklBOVk9YC4p+zkwoyT7ECg==}
+
+  '@volar/language-server@2.4.22':
+    resolution: {integrity: sha512-THIGWcQsEJKZU7SjVKPcy4MIamX4qpusKErj33ru7fi2WcD+FmFjYY/F2LIk/C15xEcb34JT1uZBlbO2dfzYSQ==}
+
+  '@volar/language-service@2.4.22':
+    resolution: {integrity: sha512-8TmvOf/6uqaJMBVQIP9kgVpRzMrqLI3nCmWuSIPAldlmwjZTOiN17GA4AL4sTFJUg61xCSyMQWbProNFQ88yew==}
+
+  '@volar/source-map@2.4.22':
+    resolution: {integrity: sha512-L2nVr/1vei0xKRgO2tYVXtJYd09HTRjaZi418e85Q+QdbbqA8h7bBjfNyPPSsjnrOO4l4kaAo78c8SQUAdHvgA==}
+
+  '@volar/typescript@2.4.22':
+    resolution: {integrity: sha512-6ZczlJW1/GWTrNnkmZxJp4qyBt/SGVlcTuCWpI5zLrdPdCZsj66Aff9ZsfFaT3TyjG8zVYgBMYPuCm/eRkpcpQ==}
+
+  '@vue-macros/api@3.0.0-beta.21':
+    resolution: {integrity: sha512-LFjWN7sJUXBVCbO4ewuq8LwEYAWCTDSsee+nqrtdlicpRavC9UQ6JWjrnFhH7apJCiKl7qpHKWr/D4+yqJra7w==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/better-define@3.0.0-beta.21':
+    resolution: {integrity: sha512-RQRKH707PTJFSPG2jsHi3hzX8iVPYrcL8H9Eg3Oxn55rv+0J6yTK4MlzZ1WU576lDk+B5sLJ1hDP3B4Vp6w9mg==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/boolean-prop@3.0.0-beta.21':
+    resolution: {integrity: sha512-8G2LM35vre8pDRmlptc+K69F8FVUTxMb2LmF1WbW3VqlcfFEIGWyFfhZ9NXiUrExaD+Eir+C70zJRitjLzChDQ==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/chain-call@3.0.0-beta.21':
+    resolution: {integrity: sha512-fdsP9FwSDH4/HmRBnWhoIDGyDDAW3OuRglQDog/scOTbEpcA1yErAqqxB8daImxsnfqk2ZTpR9UFtze9VHQO1g==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/common@3.0.0-beta.21':
+    resolution: {integrity: sha512-dK02Z8vSxWaFlJfPSxAoCKe1QgE6T/fWQ/7B2fdn2qN5AKpDlXmKriey2GTUxY9FPy1OZC4G5XxKWW0qieanSQ==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+    peerDependenciesMeta:
+      vue:
+        optional: true
+
+  '@vue-macros/config@3.0.0-beta.21':
+    resolution: {integrity: sha512-SmPE04hN43axNiNn1mM1pQUDcIwEF/DkrbUDXu9WVdlcWg7g3R3oaTGqnR3zB2hessTN4lOHwPFSCSaPUVeQdw==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/define-emit@3.0.0-beta.21':
+    resolution: {integrity: sha512-hRUmzielCeZH3D9NJzPEtLDz92gZsBdhbXD1yHEW2CUIeoKfDZ5yv5aX46lsOXyu3P9kD+L63GvQPduuA1VXYQ==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/define-models@3.0.0-beta.21':
+    resolution: {integrity: sha512-+bsOK2mW+bsx218ydfcFuS7Iqro6vnMJf8WS6QKZWD0pUWIb/Hp5+TO19DslXKFIwL5ipmHOK/avCyfuMVZ4Uw==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      '@vueuse/core': '>=9.0.0'
+    peerDependenciesMeta:
+      '@vueuse/core':
+        optional: true
+
+  '@vue-macros/define-prop@3.0.0-beta.21':
+    resolution: {integrity: sha512-zVfX50QXYyCSc6e5L757klKCkfcaAq/EX34LtdiMcVK6hNj89IbjUGgVxBIAg9q+0sGIHCq2CUI0YQTzFmuKGg==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/define-props-refs@3.0.0-beta.21':
+    resolution: {integrity: sha512-YuX3E5kTwQIXHWjLS9U9XjNGvkZtAPI/7OfW3UPEXminhOGZnyQflrRiAm69P6omNZSWxPs4GbzAsQ06FeS0Kg==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/define-props@3.0.0-beta.21':
+    resolution: {integrity: sha512-it+zXrhtX+wrW8S+0LLMe55PNs3B17fTVqVszzT3qLPAi9vAu9nE9E3zePbyjN3A8ybWSyyqccG5j/qh9P9mjQ==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      '@vue-macros/reactivity-transform': ^3.0.0-beta.21
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/define-render@3.0.0-beta.21':
+    resolution: {integrity: sha512-4eE/hzxltpAwgNAGarIOsb4PoLCfeiEFuFhZdvWDDv6xAqPSNC3k4+9OhBFsT1xg/P7NRT4O/egaM5LNDVFpCQ==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.0.0
+
+  '@vue-macros/define-slots@3.0.0-beta.21':
+    resolution: {integrity: sha512-6cO5oLCV/csPCy1bK0VE+wabJZ3t1WZugPY/1eYi81rDbCkpNwfQCS60ZwMobPXHxmgoaU5JsCtTfuybE8WopA==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.0.0
+
+  '@vue-macros/define-stylex@3.0.0-beta.21':
+    resolution: {integrity: sha512-ch7KweMR/yIlCYGvWlLUMJ8AkfuVWRq2TQAb+OyyuPY2w+/wHBkmZuVbs7TQXX54lp7Q6YWnJcqXtdBQth/lMg==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/devtools@3.0.0-beta.21':
+    resolution: {integrity: sha512-gXtNBXc3TgB7NHXjH9PgvwxqA12dVFugk7nXpNW68y5R7KcNGKtBue6drqLdQu/8jM5AKLfhoGtBDyYEmJRgkA==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vite: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+    peerDependenciesMeta:
+      vite:
+        optional: true
+
+  '@vue-macros/export-expose@3.0.0-beta.21':
+    resolution: {integrity: sha512-UxXcR3eXLFUpnVxrD9fL1s8K6LEOGUj/JqkX2awO3TAyftxLcjORh1XBKhm8xKEZtXsb62K6VVb9uJs3iUQgfA==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/export-props@3.0.0-beta.21':
+    resolution: {integrity: sha512-sizbUoijb2IhiTAChjNPJwgJqqmoN7htANP/fKIsj2MODUeAfm8uA7GhWpSi8Dskiwz7xl+D8G7cyAQTweYVfg==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/export-render@3.0.0-beta.21':
+    resolution: {integrity: sha512-Kgpq4bYzS7CbQnwB/xjMjaaNqykLZPdLjQGMHdfaKT0y9XnEJGn9hP8YvNyD8vPL1QozQ4iaLPa4mkePcnyTog==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/hoist-static@3.0.0-beta.21':
+    resolution: {integrity: sha512-0OBMzL/+UP3jfJqVzcS/WwnpLPqv16ph7izO6jasYwKcJxDRhMWBfTI/h/k5FQb7XEnZ61bk5M0hf/BcRe/L0A==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/jsx-directive@3.0.0-beta.21':
+    resolution: {integrity: sha512-28z7nZpx1CFnq6ETMTTh/POdhOUIoRW61Mdp2QuJfxwc0bDjIZSuGAz+SBHLRCY0YOK9ZZFQhc1eDiVTGBhJgQ==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/named-template@3.0.0-beta.21':
+    resolution: {integrity: sha512-45t57v1oTKq3JxPLFwkFpSE9ZQhGeEBHJN+zQf7IhlsOE5MoTIaWfaSOa/vS1GhFLCHVAuzhSky9q03dJTxiCA==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/reactivity-transform@3.0.0-beta.21':
+    resolution: {integrity: sha512-NWWgactSPDvKcu6WIc0vbYCFwcjtk52j5rWWCRwZuJJMpA8d7llR79zl/vmxJr11OdjWJR2q+L/PiD4+LZpr9A==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  '@vue-macros/script-lang@3.0.0-beta.21':
+    resolution: {integrity: sha512-yGKIZuu8Pg3FLRzf2qLCGCOatOrPyi2Izkr4DmKk45DMlskbavGsiGPT7/pF+f/V+4FFDIhG6VEWzGRNb3iRrg==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.0.0
+
+  '@vue-macros/setup-block@3.0.0-beta.21':
+    resolution: {integrity: sha512-bVJO6TEh5joliXLiKFp6MWVDGeNKYRhn+e7soMVJNWQoj+RVdtadcbw1Hym0X/ndKropLcS+yeHNFnIErG1cCQ==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/setup-component@3.0.0-beta.21':
+    resolution: {integrity: sha512-YU5XMTUCy4RXmAHAPyKt+PE4QybsmaXFTdU7PH9KqXZn0mD8y2gypBImYJt41oYZ/z4wf3ck5ouPIPWBj84VvQ==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/setup-sfc@3.0.0-beta.21':
+    resolution: {integrity: sha512-Yz9KTDJnGjxtywDPCg9+II+6SSMDfQbarGzG0Ah7JHFyM9p4UbZUlFGM8TAfvS8zReLO0SVwwZ+R4I0VJPAY6Q==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/short-bind@3.0.0-beta.21':
+    resolution: {integrity: sha512-pHXEWHPcq473vXTw0riLp8o8hRAOftecfNK2qfckFsf/6rcQlhCu2ueTmqmWO84ltyPFrhe6VhTU1968pKh+ng==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/short-emits@3.0.0-beta.21':
+    resolution: {integrity: sha512-/x/Zrkeoc+MRSq22IqWn5DIk56v+Xrp4+t+UN92t98tsTO6Q2hudJjxzbx7syiLyIOP4MZD74Ss2y14I3IJZUg==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/short-vmodel@3.0.0-beta.21':
+    resolution: {integrity: sha512-Kuqh13QCZGQxtd5OBXwRKRnu5HJlBuqBxf5ylyNmmONtwCoRKwlqAvQfbmELahB58lHYs22yc2YRtfK96O17Bg==}
+    engines: {node: '>=20.18.0'}
+
+  '@vue-macros/volar@3.0.0-beta.21':
+    resolution: {integrity: sha512-KZslp65cnij4JW0QzPPCuF75HvknLfbFDa30Qpt6NTH5T3My/BzK5SPrAyq9dgRl2jBYPDpK32uoAcbA89sM2g==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue-tsc: 3.0.5
+    peerDependenciesMeta:
+      vue-tsc:
+        optional: true
+
+  '@vue-vine/compiler@1.7.0':
+    resolution: {integrity: sha512-xchiNuxAXJAyqY5y4WjlfOn7OHSJvXEUX3nzHXSK7etip1QXGnBTvt70rD+ZeuIcrUtPPv8YatXLBmns7k6Fug==}
+
+  '@vue-vine/language-service@1.7.0':
+    resolution: {integrity: sha512-xZozKBDdila/SurUtc1bkRojc2aoqNa/SlWeIUPpDAHv7FU7T5ryuEAfdmNrXqm8kOThVcxqIJxbjHu+xvwUXg==}
+
+  '@vue/babel-helper-vue-transform-on@1.4.0':
+    resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==}
+
+  '@vue/babel-plugin-jsx@1.4.0':
+    resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    peerDependenciesMeta:
+      '@babel/core':
+        optional: true
+
+  '@vue/babel-plugin-resolve-type@1.4.0':
+    resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@vue/compiler-core@3.5.18':
+    resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==}
+
+  '@vue/compiler-dom@3.5.18':
+    resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==}
+
+  '@vue/compiler-sfc@3.5.18':
+    resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==}
+
+  '@vue/compiler-ssr@3.5.18':
+    resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==}
+
+  '@vue/compiler-vue2@2.7.16':
+    resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+  '@vue/devtools-api@6.6.4':
+    resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+  '@vue/devtools-api@7.7.7':
+    resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
+
+  '@vue/devtools-core@7.7.7':
+    resolution: {integrity: sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==}
+    peerDependencies:
+      vue: ^3.0.0
+
+  '@vue/devtools-kit@7.7.7':
+    resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
+
+  '@vue/devtools-shared@7.7.7':
+    resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
+
+  '@vue/eslint-config-prettier@10.2.0':
+    resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==}
+    peerDependencies:
+      eslint: '>= 8.21.0'
+      prettier: '>= 3.0.0'
+
+  '@vue/language-core@3.0.5':
+    resolution: {integrity: sha512-gCEjn9Ik7I/seHVNIEipOm8W+f3/kg60e8s1IgIkMYma2wu9ZGUTMv3mSL2bX+Md2L8fslceJ4SU8j1fgSRoiw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/reactivity@3.5.18':
+    resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==}
+
+  '@vue/runtime-core@3.5.18':
+    resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==}
+
+  '@vue/runtime-dom@3.5.18':
+    resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==}
+
+  '@vue/server-renderer@3.5.18':
+    resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==}
+    peerDependencies:
+      vue: 3.5.18
+
+  '@vue/shared@3.5.18':
+    resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==}
+
+  '@vue/test-utils@2.4.6':
+    resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
+
+  '@vue/tsconfig@0.7.0':
+    resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==}
+    peerDependencies:
+      typescript: 5.x
+      vue: ^3.4.0
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+      vue:
+        optional: true
+
+  '@vueuse/components@13.6.0':
+    resolution: {integrity: sha512-wJmXlQhCRWBqpnRW4KVbbKqCW28AZ/5hoA/gy7cvTXJUiRdKKfjHFo+7MGwn80dFfIkCA5253T7Y3fbNK4/lUw==}
+    peerDependencies:
+      vue: ^3.5.0
+
+  '@vueuse/core@13.6.0':
+    resolution: {integrity: sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==}
+    peerDependencies:
+      vue: ^3.5.0
+
+  '@vueuse/metadata@13.6.0':
+    resolution: {integrity: sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ==}
+
+  '@vueuse/shared@13.6.0':
+    resolution: {integrity: sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==}
+    peerDependencies:
+      vue: ^3.5.0
+
+  '@webgpu/types@0.1.64':
+    resolution: {integrity: sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==}
+
+  abbrev@2.0.0:
+    resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  acorn-jsx@5.3.2:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+  acorn@8.15.0:
+    resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  address@2.0.3:
+    resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==}
+    engines: {node: '>= 16.0.0'}
+
+  agent-base@7.1.4:
+    resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+    engines: {node: '>= 14'}
+
+  ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+  alien-signals@2.0.6:
+    resolution: {integrity: sha512-P3TxJSe31bUHBiblg59oU1PpaWPtmxF9GhJ/cB7OkgJ0qN/ifFSKUI25/v8ZhsT+lIG6ac8DpTOplXxORX6F3Q==}
+
+  ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+
+  ansi-regex@6.1.0:
+    resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+    engines: {node: '>=12'}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  ansi-styles@6.2.1:
+    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+    engines: {node: '>=12'}
+
+  ansis@4.1.0:
+    resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==}
+    engines: {node: '>=14'}
+
+  anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+
+  are-docs-informative@0.0.2:
+    resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==}
+    engines: {node: '>=14'}
+
+  argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+  assertion-error@2.0.1:
+    resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+    engines: {node: '>=12'}
+
+  ast-kit@2.1.1:
+    resolution: {integrity: sha512-mfh6a7gKXE8pDlxTvqIc/syH/P3RkzbOF6LeHdcKztLEzYe6IMsRCL7N8vI7hqTGWNxpkCuuRTpT21xNWqhRtQ==}
+    engines: {node: '>=20.18.0'}
+
+  ast-walker-scope@0.8.1:
+    resolution: {integrity: sha512-72XOdbzQCMKERvFrxAykatn2pu7osPNq/sNUzwcHdWzwPvOsNpPqkawfDXVvQbA2RT+ivtsMNjYdojTUZitt1A==}
+    engines: {node: '>=20.18.0'}
+
+  async-validator@4.2.5:
+    resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
+
+  async@3.2.6:
+    resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+    engines: {node: '>=8'}
+
+  birpc@2.5.0:
+    resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==}
+
+  boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+  brace-expansion@1.1.12:
+    resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+  brace-expansion@2.0.2:
+    resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+  braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+
+  browserslist@4.25.1:
+    resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  buffer-builder@0.2.0:
+    resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==}
+
+  buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  builtin-modules@5.0.0:
+    resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==}
+    engines: {node: '>=18.20'}
+
+  bundle-name@4.1.0:
+    resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+    engines: {node: '>=18'}
+
+  cac@6.7.14:
+    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+    engines: {node: '>=8'}
+
+  callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+
+  camel-case@4.1.2:
+    resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
+
+  caniuse-lite@1.0.30001731:
+    resolution: {integrity: sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==}
+
+  ccount@2.0.1:
+    resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+  chai@5.2.1:
+    resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==}
+    engines: {node: '>=18'}
+
+  chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+
+  change-case@5.4.4:
+    resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
+
+  character-entities@2.0.2:
+    resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+  check-error@2.1.1:
+    resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+    engines: {node: '>= 16'}
+
+  chokidar@3.6.0:
+    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+    engines: {node: '>= 8.10.0'}
+
+  chokidar@4.0.3:
+    resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+    engines: {node: '>= 14.16.0'}
+
+  chroma-js@3.1.2:
+    resolution: {integrity: sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg==}
+
+  ci-info@4.3.0:
+    resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==}
+    engines: {node: '>=8'}
+
+  clean-css@5.3.3:
+    resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
+    engines: {node: '>= 10.0'}
+
+  clean-regexp@1.0.0:
+    resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
+    engines: {node: '>=4'}
+
+  code-block-writer@13.0.3:
+    resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==}
+
+  color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+
+  color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+  color-string@1.9.1:
+    resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+  colorette@2.0.20:
+    resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+
+  colorjs.io@0.5.2:
+    resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==}
+
+  commander@10.0.1:
+    resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+    engines: {node: '>=14'}
+
+  commander@11.1.0:
+    resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
+    engines: {node: '>=16'}
+
+  commander@2.20.3:
+    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+  commander@7.2.0:
+    resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+    engines: {node: '>= 10'}
+
+  commander@8.3.0:
+    resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+    engines: {node: '>= 12'}
+
+  comment-parser@1.4.1:
+    resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
+    engines: {node: '>= 12.0.0'}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  confbox@0.1.8:
+    resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+  confbox@0.2.2:
+    resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
+
+  config-chain@1.1.13:
+    resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+
+  connect-history-api-fallback@1.6.0:
+    resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==}
+    engines: {node: '>=0.8'}
+
+  consola@2.15.3:
+    resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
+
+  consola@3.4.2:
+    resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
+    engines: {node: ^14.18.0 || >=16.10.0}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  cookie-es@1.2.2:
+    resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==}
+
+  copy-anything@3.0.5:
+    resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+    engines: {node: '>=12.13'}
+
+  core-js-compat@3.45.0:
+    resolution: {integrity: sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==}
+
+  cross-spawn@7.0.6:
+    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+    engines: {node: '>= 8'}
+
+  crossws@0.3.5:
+    resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
+
+  css-render@0.15.14:
+    resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==}
+
+  css-select@4.3.0:
+    resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
+
+  css-tree@3.1.0:
+    resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+  css-what@6.2.2:
+    resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
+    engines: {node: '>= 6'}
+
+  cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  cssstyle@4.6.0:
+    resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==}
+    engines: {node: '>=18'}
+
+  csstype@3.0.11:
+    resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
+
+  csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  d3-array@3.2.4:
+    resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+    engines: {node: '>=12'}
+
+  d3-binarytree@1.0.2:
+    resolution: {integrity: sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==}
+
+  d3-color@3.1.0:
+    resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+    engines: {node: '>=12'}
+
+  d3-dispatch@3.0.1:
+    resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+    engines: {node: '>=12'}
+
+  d3-dsv@3.0.1:
+    resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  d3-ease@3.0.1:
+    resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+    engines: {node: '>=12'}
+
+  d3-fetch@3.0.1:
+    resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
+    engines: {node: '>=12'}
+
+  d3-force-3d@3.0.6:
+    resolution: {integrity: sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==}
+    engines: {node: '>=12'}
+
+  d3-force@3.0.0:
+    resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
+    engines: {node: '>=12'}
+
+  d3-format@3.1.0:
+    resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
+    engines: {node: '>=12'}
+
+  d3-geo-projection@4.0.0:
+    resolution: {integrity: sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  d3-geo@3.1.1:
+    resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
+    engines: {node: '>=12'}
+
+  d3-hierarchy@3.1.2:
+    resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
+    engines: {node: '>=12'}
+
+  d3-interpolate@3.0.1:
+    resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+    engines: {node: '>=12'}
+
+  d3-octree@1.1.0:
+    resolution: {integrity: sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==}
+
+  d3-path@3.1.0:
+    resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+    engines: {node: '>=12'}
+
+  d3-quadtree@3.0.1:
+    resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
+    engines: {node: '>=12'}
+
+  d3-random@3.0.1:
+    resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
+    engines: {node: '>=12'}
+
+  d3-regression@1.3.10:
+    resolution: {integrity: sha512-PF8GWEL70cHHWpx2jUQXc68r1pyPHIA+St16muk/XRokETzlegj5LriNKg7o4LR0TySug4nHYPJNNRz/W+/Niw==}
+
+  d3-scale-chromatic@3.1.0:
+    resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
+    engines: {node: '>=12'}
+
+  d3-scale@4.0.2:
+    resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+    engines: {node: '>=12'}
+
+  d3-shape@3.2.0:
+    resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+    engines: {node: '>=12'}
+
+  d3-time-format@4.1.0:
+    resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+    engines: {node: '>=12'}
+
+  d3-time@3.1.0:
+    resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+    engines: {node: '>=12'}
+
+  d3-timer@3.0.1:
+    resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+    engines: {node: '>=12'}
+
+  data-urls@5.0.0:
+    resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+    engines: {node: '>=18'}
+
+  date-fns-tz@3.2.0:
+    resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==}
+    peerDependencies:
+      date-fns: ^3.0.0 || ^4.0.0
+
+  date-fns@3.6.0:
+    resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
+
+  de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+  debug@4.4.1:
+    resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  decimal.js@10.6.0:
+    resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
+  decode-named-character-reference@1.2.0:
+    resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
+
+  deep-eql@5.0.2:
+    resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+    engines: {node: '>=6'}
+
+  deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+  deep-pick-omit@1.2.1:
+    resolution: {integrity: sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==}
+
+  default-browser-id@5.0.0:
+    resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
+    engines: {node: '>=18'}
+
+  default-browser@5.2.1:
+    resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
+    engines: {node: '>=18'}
+
+  define-lazy-prop@3.0.0:
+    resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
+    engines: {node: '>=12'}
+
+  defu@6.1.4:
+    resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+  dequal@2.0.3:
+    resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+    engines: {node: '>=6'}
+
+  destr@2.0.5:
+    resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
+
+  detect-libc@1.0.3:
+    resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+    engines: {node: '>=0.10'}
+    hasBin: true
+
+  detect-libc@2.0.4:
+    resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
+    engines: {node: '>=8'}
+
+  detect-port@2.1.0:
+    resolution: {integrity: sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==}
+    engines: {node: '>= 16.0.0'}
+    hasBin: true
+
+  devlop@1.1.0:
+    resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+  dom-serializer@1.4.1:
+    resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==}
+
+  domelementtype@2.3.0:
+    resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+  domhandler@4.3.1:
+    resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
+    engines: {node: '>= 4'}
+
+  domutils@2.8.0:
+    resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
+
+  dot-case@3.0.4:
+    resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
+
+  dotenv-expand@8.0.3:
+    resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==}
+    engines: {node: '>=12'}
+
+  dotenv@16.6.1:
+    resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
+    engines: {node: '>=12'}
+
+  dotenv@17.2.1:
+    resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==}
+    engines: {node: '>=12'}
+
+  duplexer@0.1.2:
+    resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
+
+  earcut@2.2.4:
+    resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==}
+
+  eastasianwidth@0.2.0:
+    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+  eciesjs@0.4.15:
+    resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==}
+    engines: {bun: '>=1', deno: '>=2', node: '>=16'}
+
+  editorconfig@1.0.4:
+    resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  ejs@3.1.10:
+    resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
+    engines: {node: '>=0.10.0'}
+    hasBin: true
+
+  electron-to-chromium@1.5.198:
+    resolution: {integrity: sha512-G5COfnp3w+ydVu80yprgWSfmfQaYRh9DOxfhAxstLyetKaLyl55QrNjx8C38Pc/C+RaDmb1M0Lk8wPEMQ+bGgQ==}
+
+  emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+  emoji-regex@9.2.2:
+    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+  enhanced-resolve@5.18.3:
+    resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
+    engines: {node: '>=10.13.0'}
+
+  entities@2.2.0:
+    resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
+
+  entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  entities@6.0.1:
+    resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+    engines: {node: '>=0.12'}
+
+  error-stack-parser-es@1.0.5:
+    resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
+
+  es-module-lexer@1.7.0:
+    resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
+
+  es-toolkit@1.39.8:
+    resolution: {integrity: sha512-A8QO9TfF+rltS8BXpdu8OS+rpGgEdnRhqIVxO/ZmNvnXBYgOdSsxukT55ELyP94gZIntWJ+Li9QRrT2u1Kitpg==}
+
+  esbuild@0.25.8:
+    resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
+  escape-string-regexp@1.0.5:
+    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+    engines: {node: '>=0.8.0'}
+
+  escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+
+  escape-string-regexp@5.0.0:
+    resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+    engines: {node: '>=12'}
+
+  eslint-compat-utils@0.5.1:
+    resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==}
+    engines: {node: '>=12'}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  eslint-compat-utils@0.6.5:
+    resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==}
+    engines: {node: '>=12'}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  eslint-config-flat-gitignore@2.1.0:
+    resolution: {integrity: sha512-cJzNJ7L+psWp5mXM7jBX+fjHtBvvh06RBlcweMhKD8jWqQw0G78hOW5tpVALGHGFPsBV+ot2H+pdDGJy6CV8pA==}
+    peerDependencies:
+      eslint: ^9.5.0
+
+  eslint-config-prettier@10.1.8:
+    resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+
+  eslint-flat-config-utils@2.1.1:
+    resolution: {integrity: sha512-K8eaPkBemHkfbYsZH7z4lZ/tt6gNSsVh535Wh9W9gQBS2WjvfUbbVr2NZR3L1yiRCLuOEimYfPxCxODczD4Opg==}
+
+  eslint-json-compat-utils@0.2.1:
+    resolution: {integrity: sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg==}
+    engines: {node: '>=12'}
+    peerDependencies:
+      '@eslint/json': '*'
+      eslint: '*'
+      jsonc-eslint-parser: ^2.4.0
+    peerDependenciesMeta:
+      '@eslint/json':
+        optional: true
+
+  eslint-merge-processors@2.0.0:
+    resolution: {integrity: sha512-sUuhSf3IrJdGooquEUB5TNpGNpBoQccbnaLHsb1XkBLUPPqCNivCpY05ZcpCOiV9uHwO2yxXEWVczVclzMxYlA==}
+    peerDependencies:
+      eslint: '*'
+
+  eslint-plugin-antfu@3.1.1:
+    resolution: {integrity: sha512-7Q+NhwLfHJFvopI2HBZbSxWXngTwBLKxW1AGXLr2lEGxcEIK/AsDs8pn8fvIizl5aZjBbVbVK5ujmMpBe4Tvdg==}
+    peerDependencies:
+      eslint: '*'
+
+  eslint-plugin-command@3.3.1:
+    resolution: {integrity: sha512-fBVTXQ2y48TVLT0+4A6PFINp7GcdIailHAXbvPBixE7x+YpYnNQhFZxTdvnb+aWk+COgNebQKen/7m4dmgyWAw==}
+    peerDependencies:
+      eslint: '*'
+
+  eslint-plugin-es-x@7.8.0:
+    resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=8'
+
+  eslint-plugin-import-lite@0.3.0:
+    resolution: {integrity: sha512-dkNBAL6jcoCsXZsQ/Tt2yXmMDoNt5NaBh/U7yvccjiK8cai6Ay+MK77bMykmqQA2bTF6lngaLCDij6MTO3KkvA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: '>=9.0.0'
+      typescript: '>=4.5'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  eslint-plugin-jsdoc@52.0.4:
+    resolution: {integrity: sha512-be5OzGlLExvcK13Il3noU7/v7WmAQGenTmCaBKf1pwVtPOb6X+PGFVnJad0QhMj4KKf45XjE4hbsBxv25q1fTg==}
+    engines: {node: '>=20.11.0'}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
+
+  eslint-plugin-jsonc@2.20.1:
+    resolution: {integrity: sha512-gUzIwQHXx7ZPypUoadcyRi4WbHW2TPixDr0kqQ4miuJBU0emJmyGTlnaT3Og9X2a8R1CDayN9BFSq5weGWbTng==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  eslint-plugin-n@17.21.3:
+    resolution: {integrity: sha512-MtxYjDZhMQgsWRm/4xYLL0i2EhusWT7itDxlJ80l1NND2AL2Vi5Mvneqv/ikG9+zpran0VsVRXTEHrpLmUZRNw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: '>=8.23.0'
+
+  eslint-plugin-no-only-tests@3.3.0:
+    resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==}
+    engines: {node: '>=5.0.0'}
+
+  eslint-plugin-perfectionist@4.15.0:
+    resolution: {integrity: sha512-pC7PgoXyDnEXe14xvRUhBII8A3zRgggKqJFx2a82fjrItDs1BSI7zdZnQtM2yQvcyod6/ujmzb7ejKPx8lZTnw==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      eslint: '>=8.45.0'
+
+  eslint-plugin-playwright@2.2.2:
+    resolution: {integrity: sha512-j0jKpndIPOXRRP9uMkwb9l/nSmModOU3452nrFdgFJoEv/435J1onk8+aITzjDW8DfypxgmVaDMdmVIa6F7I0w==}
+    engines: {node: '>=16.6.0'}
+    peerDependencies:
+      eslint: '>=8.40.0'
+
+  eslint-plugin-pnpm@1.1.0:
+    resolution: {integrity: sha512-sL93w0muBtjnogzk/loDsxzMbmXQOLP5Blw3swLDBXZgfb+qQI73bPcUbjVR+ZL+K62vGJdErV+43i3r5DsZPg==}
+    peerDependencies:
+      eslint: ^9.0.0
+
+  eslint-plugin-prettier@5.5.4:
+    resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      '@types/eslint': '>=8.0.0'
+      eslint: '>=8.0.0'
+      eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
+      prettier: '>=3.0.0'
+    peerDependenciesMeta:
+      '@types/eslint':
+        optional: true
+      eslint-config-prettier:
+        optional: true
+
+  eslint-plugin-regexp@2.9.1:
+    resolution: {integrity: sha512-JwK6glV/aoYDxvXcrvMQbw/pByBewZwqXVSBzzjot3GxSbmjDYuWU4LWiLdBO8JKi4o8A1+rygO6JWRBg4qAQQ==}
+    engines: {node: ^18 || >=20}
+    peerDependencies:
+      eslint: '>=8.44.0'
+
+  eslint-plugin-toml@0.12.0:
+    resolution: {integrity: sha512-+/wVObA9DVhwZB1nG83D2OAQRrcQZXy+drqUnFJKymqnmbnbfg/UPmEMCKrJNcEboUGxUjYrJlgy+/Y930mURQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  eslint-plugin-unicorn@60.0.0:
+    resolution: {integrity: sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==}
+    engines: {node: ^20.10.0 || >=21.0.0}
+    peerDependencies:
+      eslint: '>=9.29.0'
+
+  eslint-plugin-unused-imports@4.1.4:
+    resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==}
+    peerDependencies:
+      '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
+      eslint: ^9.0.0 || ^8.0.0
+    peerDependenciesMeta:
+      '@typescript-eslint/eslint-plugin':
+        optional: true
+
+  eslint-plugin-vue@10.2.0:
+    resolution: {integrity: sha512-tl9s+KN3z0hN2b8fV2xSs5ytGl7Esk1oSCxULLwFcdaElhZ8btYYZFrWxvh4En+czrSDtuLCeCOGa8HhEZuBdQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+      vue-eslint-parser: ^10.0.0
+
+  eslint-plugin-vue@10.4.0:
+    resolution: {integrity: sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^7.0.0 || ^8.0.0
+      eslint: ^8.57.0 || ^9.0.0
+      vue-eslint-parser: ^10.0.0
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+
+  eslint-plugin-yml@1.18.0:
+    resolution: {integrity: sha512-9NtbhHRN2NJa/s3uHchO3qVVZw0vyOIvWlXWGaKCr/6l3Go62wsvJK5byiI6ZoYztDsow4GnS69BZD3GnqH3hA==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  eslint-processor-vue-blocks@2.0.0:
+    resolution: {integrity: sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q==}
+    peerDependencies:
+      '@vue/compiler-sfc': ^3.3.0
+      eslint: '>=9.0.0'
+
+  eslint-scope@8.4.0:
+    resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint-visitor-keys@3.4.3:
+    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint-visitor-keys@4.2.1:
+    resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint@9.32.0:
+    resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    hasBin: true
+    peerDependencies:
+      jiti: '*'
+    peerDependenciesMeta:
+      jiti:
+        optional: true
+
+  espree@10.4.0:
+    resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  espree@9.6.1:
+    resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  esquery@1.6.0:
+    resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+    engines: {node: '>=0.10'}
+
+  esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+
+  estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+
+  estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  estree-walker@3.0.3:
+    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+  esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+
+  eventemitter3@5.0.1:
+    resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+
+  evtd@0.2.4:
+    resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==}
+
+  execa@5.1.1:
+    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+    engines: {node: '>=10'}
+
+  execa@9.6.0:
+    resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==}
+    engines: {node: ^18.19.0 || >=20.5.0}
+
+  expect-type@1.2.2:
+    resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
+    engines: {node: '>=12.0.0'}
+
+  exsolve@1.0.7:
+    resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
+
+  fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+  fast-diff@1.3.0:
+    resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
+  fast-glob@3.3.3:
+    resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+    engines: {node: '>=8.6.0'}
+
+  fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+  fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+  fast-npm-meta@0.4.6:
+    resolution: {integrity: sha512-zbBBOAOlzxfrU4WSnbCHk/nR6Vf32lSEPxDEvNOR08Z5DSZ/A6qJu0rqrHVcexBTd1hc2gim998xnqF/R1PuEw==}
+
+  fastq@1.19.1:
+    resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+  fault@2.0.1:
+    resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
+
+  fdir@6.4.6:
+    resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
+    peerDependencies:
+      picomatch: ^3 || ^4
+    peerDependenciesMeta:
+      picomatch:
+        optional: true
+
+  fecha@4.2.3:
+    resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
+
+  fflate@0.8.2:
+    resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
+
+  figures@6.1.0:
+    resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
+    engines: {node: '>=18'}
+
+  file-entry-cache@8.0.0:
+    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+    engines: {node: '>=16.0.0'}
+
+  filelist@1.0.4:
+    resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
+
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+
+  find-up-simple@1.0.1:
+    resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
+    engines: {node: '>=18'}
+
+  find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+
+  flat-cache@4.0.1:
+    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+    engines: {node: '>=16'}
+
+  flatted@3.3.3:
+    resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+  flru@1.0.2:
+    resolution: {integrity: sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==}
+    engines: {node: '>=6'}
+
+  foreground-child@3.3.1:
+    resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+    engines: {node: '>=14'}
+
+  format@0.2.2:
+    resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
+    engines: {node: '>=0.4.x'}
+
+  fs-extra@10.1.0:
+    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+    engines: {node: '>=12'}
+
+  fsevents@2.3.2:
+    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
+  fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  get-port-please@3.2.0:
+    resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==}
+
+  get-stream@6.0.1:
+    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+    engines: {node: '>=10'}
+
+  get-stream@9.0.1:
+    resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
+    engines: {node: '>=18'}
+
+  get-tsconfig@4.10.1:
+    resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
+
+  github-slugger@2.0.0:
+    resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
+
+  gl-matrix@3.4.3:
+    resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==}
+
+  glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+
+  glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+
+  glob@10.4.5:
+    resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+    hasBin: true
+
+  globals@13.24.0:
+    resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+    engines: {node: '>=8'}
+
+  globals@14.0.0:
+    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+    engines: {node: '>=18'}
+
+  globals@15.15.0:
+    resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
+    engines: {node: '>=18'}
+
+  globals@16.3.0:
+    resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==}
+    engines: {node: '>=18'}
+
+  globrex@0.1.2:
+    resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+
+  graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+  graphemer@1.4.0:
+    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+  gzip-size@6.0.0:
+    resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
+    engines: {node: '>=10'}
+
+  h3@1.15.4:
+    resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==}
+
+  has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+
+  hash-sum@2.0.0:
+    resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
+
+  he@1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+    hasBin: true
+
+  highlight.js@11.11.1:
+    resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
+    engines: {node: '>=12.0.0'}
+
+  hookable@5.5.3:
+    resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+  html-encoding-sniffer@4.0.0:
+    resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+    engines: {node: '>=18'}
+
+  html-minifier-terser@6.1.0:
+    resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  http-proxy-agent@7.0.2:
+    resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+    engines: {node: '>= 14'}
+
+  https-proxy-agent@7.0.6:
+    resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+    engines: {node: '>= 14'}
+
+  human-signals@2.1.0:
+    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+    engines: {node: '>=10.17.0'}
+
+  human-signals@8.0.1:
+    resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==}
+    engines: {node: '>=18.18.0'}
+
+  iconv-lite@0.6.3:
+    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+    engines: {node: '>=0.10.0'}
+
+  ignore@5.3.2:
+    resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+    engines: {node: '>= 4'}
+
+  ignore@7.0.5:
+    resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+    engines: {node: '>= 4'}
+
+  immutable@5.1.3:
+    resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==}
+
+  import-fresh@3.3.1:
+    resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+    engines: {node: '>=6'}
+
+  imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+
+  indent-string@5.0.0:
+    resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
+    engines: {node: '>=12'}
+
+  ini@1.3.8:
+    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+  internmap@2.0.3:
+    resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+    engines: {node: '>=12'}
+
+  iron-webcrypto@1.2.1:
+    resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
+
+  is-arrayish@0.3.2:
+    resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+
+  is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+
+  is-builtin-module@5.0.0:
+    resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==}
+    engines: {node: '>=18.20'}
+
+  is-docker@3.0.0:
+    resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    hasBin: true
+
+  is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+
+  is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+
+  is-inside-container@1.0.0:
+    resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
+    engines: {node: '>=14.16'}
+    hasBin: true
+
+  is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  is-plain-obj@4.1.0:
+    resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+    engines: {node: '>=12'}
+
+  is-potential-custom-element-name@1.0.1:
+    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+  is-stream@2.0.1:
+    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+    engines: {node: '>=8'}
+
+  is-stream@4.0.1:
+    resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
+    engines: {node: '>=18'}
+
+  is-unicode-supported@2.1.0:
+    resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
+    engines: {node: '>=18'}
+
+  is-what@4.1.16:
+    resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+    engines: {node: '>=12.13'}
+
+  is-wsl@3.1.0:
+    resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
+    engines: {node: '>=16'}
+
+  isarray@1.0.0:
+    resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
+  isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+  isexe@3.1.1:
+    resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
+    engines: {node: '>=16'}
+
+  isobject@2.1.0:
+    resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==}
+    engines: {node: '>=0.10.0'}
+
+  jackspeak@3.4.3:
+    resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+  jake@10.9.4:
+    resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  jiti@2.5.1:
+    resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
+    hasBin: true
+
+  js-beautify@1.15.4:
+    resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  js-cookie@3.0.5:
+    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+    engines: {node: '>=14'}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  js-tokens@9.0.1:
+    resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
+  js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+
+  jsdoc-type-pratt-parser@4.1.0:
+    resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
+    engines: {node: '>=12.0.0'}
+
+  jsdom@26.1.0:
+    resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==}
+    engines: {node: '>=18'}
+    peerDependencies:
+      canvas: ^3.0.0
+    peerDependenciesMeta:
+      canvas:
+        optional: true
+
+  jsesc@3.0.2:
+    resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsesc@3.1.0:
+    resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  json-buffer@3.0.1:
+    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+  json-parse-even-better-errors@4.0.0:
+    resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+  json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsonc-eslint-parser@2.4.0:
+    resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+  kolorist@1.8.0:
+    resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+  launch-editor@2.11.0:
+    resolution: {integrity: sha512-R/PIF14L6e2eHkhvQPu7jDRCr0msfCYCxbYiLgkkAGi0dVPWuM+RrsPu0a5dpuNe0KWGL3jpAkOlv53xGfPheQ==}
+
+  levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+
+  lightningcss-darwin-arm64@1.30.1:
+    resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  lightningcss-darwin-x64@1.30.1:
+    resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  lightningcss-freebsd-x64@1.30.1:
+    resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [freebsd]
+
+  lightningcss-linux-arm-gnueabihf@1.30.1:
+    resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm]
+    os: [linux]
+
+  lightningcss-linux-arm64-gnu@1.30.1:
+    resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  lightningcss-linux-arm64-musl@1.30.1:
+    resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  lightningcss-linux-x64-gnu@1.30.1:
+    resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  lightningcss-linux-x64-musl@1.30.1:
+    resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  lightningcss-win32-arm64-msvc@1.30.1:
+    resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  lightningcss-win32-x64-msvc@1.30.1:
+    resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [win32]
+
+  lightningcss@1.30.1:
+    resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+    engines: {node: '>= 12.0.0'}
+
+  line-column@1.0.2:
+    resolution: {integrity: sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==}
+
+  local-pkg@1.1.1:
+    resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
+    engines: {node: '>=14'}
+
+  locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+
+  lodash-es@4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+  lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  longest-streak@3.1.0:
+    resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+  loupe@3.2.0:
+    resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==}
+
+  lower-case@2.0.2:
+    resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+
+  lru-cache@10.4.3:
+    resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+  lru-cache@11.1.0:
+    resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==}
+    engines: {node: 20 || >=22}
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  magic-string-ast@1.0.0:
+    resolution: {integrity: sha512-8rbuNizut2gW94kv7pqgt0dvk+AHLPVIm0iJtpSgQJ9dx21eWx5SBel8z3jp1xtC0j6/iyK3AWGhAR1H61s7LA==}
+    engines: {node: '>=20.18.0'}
+
+  magic-string@0.30.17:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+  markdown-table@3.0.4:
+    resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
+  mdast-util-find-and-replace@3.0.2:
+    resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
+  mdast-util-from-markdown@2.0.2:
+    resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
+
+  mdast-util-frontmatter@2.0.1:
+    resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==}
+
+  mdast-util-gfm-autolink-literal@2.0.1:
+    resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+  mdast-util-gfm-footnote@2.1.0:
+    resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+  mdast-util-gfm-strikethrough@2.0.0:
+    resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+  mdast-util-gfm-table@2.0.0:
+    resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+  mdast-util-gfm-task-list-item@2.0.0:
+    resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+  mdast-util-gfm@3.1.0:
+    resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
+  mdast-util-phrasing@4.1.0:
+    resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+  mdast-util-to-markdown@2.1.2:
+    resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
+
+  mdast-util-to-string@4.0.0:
+    resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+  mdn-data@2.12.2:
+    resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
+
+  memorystream@0.3.1:
+    resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
+    engines: {node: '>= 0.10.0'}
+
+  merge-source-map@1.1.0:
+    resolution: {integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==}
+
+  merge-stream@2.0.0:
+    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+  merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  micromark-core-commonmark@2.0.3:
+    resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+
+  micromark-extension-frontmatter@2.0.0:
+    resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==}
+
+  micromark-extension-gfm-autolink-literal@2.1.0:
+    resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+  micromark-extension-gfm-footnote@2.1.0:
+    resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+  micromark-extension-gfm-strikethrough@2.1.0:
+    resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+  micromark-extension-gfm-table@2.1.1:
+    resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
+
+  micromark-extension-gfm-tagfilter@2.0.0:
+    resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+  micromark-extension-gfm-task-list-item@2.1.0:
+    resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+  micromark-extension-gfm@3.0.0:
+    resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+  micromark-factory-destination@2.0.1:
+    resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+  micromark-factory-label@2.0.1:
+    resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+  micromark-factory-space@2.0.1:
+    resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
+
+  micromark-factory-title@2.0.1:
+    resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+  micromark-factory-whitespace@2.0.1:
+    resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+  micromark-util-character@2.1.1:
+    resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+  micromark-util-chunked@2.0.1:
+    resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+  micromark-util-classify-character@2.0.1:
+    resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+  micromark-util-combine-extensions@2.0.1:
+    resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+  micromark-util-decode-numeric-character-reference@2.0.2:
+    resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+  micromark-util-decode-string@2.0.1:
+    resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
+
+  micromark-util-encode@2.0.1:
+    resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+  micromark-util-html-tag-name@2.0.1:
+    resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+  micromark-util-normalize-identifier@2.0.1:
+    resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+  micromark-util-resolve-all@2.0.1:
+    resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+  micromark-util-sanitize-uri@2.0.1:
+    resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+  micromark-util-subtokenize@2.1.0:
+    resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+  micromark-util-symbol@2.0.1:
+    resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+  micromark-util-types@2.0.2:
+    resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+  micromark@4.0.2:
+    resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
+
+  micromatch@4.0.8:
+    resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+    engines: {node: '>=8.6'}
+
+  mimic-fn@2.1.0:
+    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+    engines: {node: '>=6'}
+
+  min-indent@1.0.1:
+    resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+    engines: {node: '>=4'}
+
+  minimatch@10.0.3:
+    resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==}
+    engines: {node: 20 || >=22}
+
+  minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+  minimatch@5.1.6:
+    resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+    engines: {node: '>=10'}
+
+  minimatch@9.0.1:
+    resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minimatch@9.0.5:
+    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minipass@7.1.2:
+    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  mitt@3.0.1:
+    resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+  mlly@1.7.4:
+    resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
+
+  mri@1.2.0:
+    resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+    engines: {node: '>=4'}
+
+  mrmime@2.0.1:
+    resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+    engines: {node: '>=10'}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  muggle-string@0.4.1:
+    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+  naive-ui@2.42.0:
+    resolution: {integrity: sha512-c7cXR2YgOjgtBadXHwiWL4Y0tpGLAI5W5QzzHksOi22iuHXoSGMAzdkVTGVPE/PM0MSGQ/JtUIzCx2Y0hU0vTQ==}
+    peerDependencies:
+      vue: ^3.0.0
+
+  nanoid@3.3.11:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  nanoid@5.1.5:
+    resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
+    engines: {node: ^18 || >=20}
+    hasBin: true
+
+  napi-postinstall@0.3.2:
+    resolution: {integrity: sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==}
+    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+    hasBin: true
+
+  natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+  natural-orderby@5.0.0:
+    resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==}
+    engines: {node: '>=18'}
+
+  neverthrow@8.2.0:
+    resolution: {integrity: sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ==}
+    engines: {node: '>=18'}
+
+  no-case@3.0.4:
+    resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
+
+  node-addon-api@7.1.1:
+    resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+  node-fetch-native@1.6.7:
+    resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
+
+  node-html-parser@5.4.2:
+    resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==}
+
+  node-mock-http@1.0.2:
+    resolution: {integrity: sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==}
+
+  node-modules-inspector@1.0.0:
+    resolution: {integrity: sha512-7u+diiGdYLC0ij3eqifVdQ4AlxB/5aaPnoVXhlhw+gxLIBbT3ZagsXoUWsl0B5Px17VfHffVhCtVx9nu5nxnvQ==}
+    hasBin: true
+
+  node-modules-tools@1.0.0:
+    resolution: {integrity: sha512-5XS4qAAdv8SIEH1tUisfe25EIituEkoLEQOYLtJdi9isdapx6dzKAYvB0GJpUcYU7W3pQoe3Tpnv9qpe8xK6nA==}
+
+  node-releases@2.0.19:
+    resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+
+  nopt@7.2.1:
+    resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    hasBin: true
+
+  normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  npm-normalize-package-bin@4.0.0:
+    resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  npm-run-all2@8.0.4:
+    resolution: {integrity: sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==}
+    engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'}
+    hasBin: true
+
+  npm-run-path@4.0.1:
+    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+    engines: {node: '>=8'}
+
+  npm-run-path@6.0.0:
+    resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
+    engines: {node: '>=18'}
+
+  nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+  nwsapi@2.2.21:
+    resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==}
+
+  object-treeify@1.1.33:
+    resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==}
+    engines: {node: '>= 10'}
+
+  ofetch@1.4.1:
+    resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
+
+  ohash@2.0.11:
+    resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
+
+  onetime@5.1.2:
+    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+    engines: {node: '>=6'}
+
+  open@10.2.0:
+    resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
+    engines: {node: '>=18'}
+
+  optionator@0.9.4:
+    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+    engines: {node: '>= 0.8.0'}
+
+  oxc-resolver@11.6.1:
+    resolution: {integrity: sha512-WQgmxevT4cM5MZ9ioQnEwJiHpPzbvntV5nInGAKo9NQZzegcOonHvcVcnkYqld7bTG35UFHEKeF7VwwsmA3cZg==}
+
+  p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+
+  p-limit@6.2.0:
+    resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==}
+    engines: {node: '>=18'}
+
+  p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+
+  package-json-from-dist@1.0.1:
+    resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+  package-manager-detector@1.3.0:
+    resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==}
+
+  param-case@3.0.4:
+    resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
+
+  parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+
+  parse-gitignore@2.0.0:
+    resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==}
+    engines: {node: '>=14'}
+
+  parse-imports-exports@0.2.4:
+    resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==}
+
+  parse-ms@4.0.0:
+    resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
+    engines: {node: '>=18'}
+
+  parse-statements@1.0.11:
+    resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==}
+
+  parse5@7.3.0:
+    resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
+
+  pascal-case@3.1.2:
+    resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
+
+  path-browserify@1.0.1:
+    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+  path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+
+  path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+
+  path-key@4.0.0:
+    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+    engines: {node: '>=12'}
+
+  path-scurry@1.11.1:
+    resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+    engines: {node: '>=16 || 14 >=14.18'}
+
+  pathe@0.2.0:
+    resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
+
+  pathe@2.0.3:
+    resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+  pathval@2.0.1:
+    resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
+    engines: {node: '>= 14.16'}
+
+  pdfast@0.2.0:
+    resolution: {integrity: sha512-cq6TTu6qKSFUHwEahi68k/kqN2mfepjkGrG9Un70cgdRRKLKY6Rf8P8uvP2NvZktaQZNF3YE7agEkLj0vGK9bA==}
+
+  perfect-debounce@1.0.0:
+    resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  picomatch@4.0.3:
+    resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+    engines: {node: '>=12'}
+
+  pidtree@0.6.0:
+    resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+    engines: {node: '>=0.10'}
+    hasBin: true
+
+  pinia-plugin-persistedstate@4.4.1:
+    resolution: {integrity: sha512-lmuMPpXla2zJKjxEq34e1E9P9jxkWEhcVwwioCCE0izG45kkTOvQfCzvwhW3i38cvnaWC7T1eRdkd15Re59ldw==}
+    peerDependencies:
+      '@nuxt/kit': '>=3.0.0'
+      '@pinia/nuxt': '>=0.10.0'
+      pinia: '>=3.0.0'
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+      '@pinia/nuxt':
+        optional: true
+      pinia:
+        optional: true
+
+  pinia@3.0.3:
+    resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==}
+    peerDependencies:
+      typescript: '>=4.4.4'
+      vue: ^2.7.0 || ^3.5.11
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  pkg-types@1.3.1:
+    resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+  pkg-types@2.2.0:
+    resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==}
+
+  playwright-core@1.54.2:
+    resolution: {integrity: sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  playwright@1.54.2:
+    resolution: {integrity: sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  pluralize@8.0.0:
+    resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
+    engines: {node: '>=4'}
+
+  pnpm-workspace-yaml@1.1.0:
+    resolution: {integrity: sha512-OWUzBxtitpyUV0fBYYwLAfWxn3mSzVbVB7cwgNaHvTTU9P0V2QHjyaY5i7f1hEiT9VeKsNH1Skfhe2E3lx/zhA==}
+
+  postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+    engines: {node: '>=4'}
+
+  postcss-selector-parser@7.1.0:
+    resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==}
+    engines: {node: '>=4'}
+
+  postcss@8.5.6:
+    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+
+  prettier-linter-helpers@1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+
+  prettier@3.5.3:
+    resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  pretty-ms@9.2.0:
+    resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
+    engines: {node: '>=18'}
+
+  proto-list@1.2.4:
+    resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+
+  publint@0.3.12:
+    resolution: {integrity: sha512-1w3MMtL9iotBjm1mmXtG3Nk06wnq9UhGNRpQ2j6n1Zq7YAD6gnxMMZMIxlRPAydVjVbjSm+n0lhwqsD1m4LD5w==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
+  quansync@0.2.10:
+    resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
+
+  queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+  quickselect@2.0.0:
+    resolution: {integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==}
+
+  radix3@1.1.2:
+    resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
+
+  rbush@3.0.1:
+    resolution: {integrity: sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==}
+
+  read-package-json-fast@4.0.0:
+    resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+
+  readdirp@4.1.2:
+    resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+    engines: {node: '>= 14.18.0'}
+
+  refa@0.12.1:
+    resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  regexp-ast-analysis@0.7.1:
+    resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  regexp-tree@0.1.27:
+    resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
+    hasBin: true
+
+  regjsparser@0.12.0:
+    resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==}
+    hasBin: true
+
+  relateurl@0.2.7:
+    resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
+    engines: {node: '>= 0.10'}
+
+  request-light@0.7.0:
+    resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==}
+
+  resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+
+  resolve-pkg-maps@1.0.0:
+    resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+  reusify@1.1.0:
+    resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  rfdc@1.4.1:
+    resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+  rolldown-vite@7.1.0:
+    resolution: {integrity: sha512-DCfutVwSkuc3NA75SdFuetKwcpq97tO2JZdM6gYv7GwW6/9qyXnITYER+8zrJt+vQVGP2SadAkMOpWw7B4LVwg==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^20.19.0 || >=22.12.0
+      esbuild: ^0.25.0
+      jiti: '>=1.21.0'
+      less: ^4.0.0
+      sass: ^1.70.0
+      sass-embedded: ^1.70.0
+      stylus: '>=0.54.8'
+      sugarss: ^5.0.0
+      terser: ^5.16.0
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      esbuild:
+        optional: true
+      jiti:
+        optional: true
+      less:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+  rolldown@1.0.0-beta.31:
+    resolution: {integrity: sha512-M2Q+RfG0FMJeSW3RSFTbvtjGVTcQpTQvN247D0EMSsPkpZFoinopR9oAnQiwgogQyzDuvKNnbyCbQQlmNAzSoQ==}
+    hasBin: true
+
+  rollup@4.46.2:
+    resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  rrweb-cssom@0.8.0:
+    resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+
+  run-applescript@7.0.0:
+    resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
+    engines: {node: '>=18'}
+
+  run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+  rw@1.3.3:
+    resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+
+  rxjs@7.8.2:
+    resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+
+  sade@1.8.1:
+    resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
+    engines: {node: '>=6'}
+
+  safer-buffer@2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+  sass-embedded-all-unknown@1.90.0:
+    resolution: {integrity: sha512-/n7jTQvI+hftDDrHzK19G4pxfDzOhtjuQO1K54ui1pT2S0sWfWDjCYUbQgtWQ6FO7g5qWS0hgmrWdc7fmS3rgA==}
+    cpu: ['!arm', '!arm64', '!riscv64', '!x64']
+
+  sass-embedded-android-arm64@1.90.0:
+    resolution: {integrity: sha512-bkTlewzWksa6Sj4Zs1CWiutnvUbsO3xuYh2QBRknXsOtuMlfTPoXnwhCnyE4lSvUxw2qxSbv+NdQev9qMfsBgA==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm64]
+    os: [android]
+
+  sass-embedded-android-arm@1.90.0:
+    resolution: {integrity: sha512-usF6kVJQWa1CMgPH1nCT1y8KEmAT2fzB00dDIPBYHq8U5VZLCihi2bJRP5U9NlcwP1TlKGKCjwsbIdSjDKfecg==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm]
+    os: [android]
+
+  sass-embedded-android-riscv64@1.90.0:
+    resolution: {integrity: sha512-bpqCIOaX+0Lou/BNJ4iJIKbWbVaYXFdg26C3gG6gxxKZRzp/6OYCxHrIQDwhKz6YC8Q5rwNPMpfDVYbWPcgroA==}
+    engines: {node: '>=14.0.0'}
+    cpu: [riscv64]
+    os: [android]
+
+  sass-embedded-android-x64@1.90.0:
+    resolution: {integrity: sha512-GNxVKnCMd/p2icZ+Q4mhvNk19NrLXq1C4guiqjrycHYQLEnxRkjbW1QXYiL+XyDn4e+Bcq0knzG0I9pMuNZxkg==}
+    engines: {node: '>=14.0.0'}
+    cpu: [x64]
+    os: [android]
+
+  sass-embedded-darwin-arm64@1.90.0:
+    resolution: {integrity: sha512-qr4KBMJfBA+lzXiWnP00qzpLzHQzGd1OSK3VHcUFjZ8l7VOYf2R7Tc3fcTLhpaNPMJtTK0jrk8rFqBvsiZExnA==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  sass-embedded-darwin-x64@1.90.0:
+    resolution: {integrity: sha512-z2nr1nNqtWDLVRwTbHtL7zriK90U7O/Gb15UaCSMYeAz9Y+wog5s/sDEKm0+GsVdzzkaCaMZRWGN4jTilnUwmQ==}
+    engines: {node: '>=14.0.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  sass-embedded-linux-arm64@1.90.0:
+    resolution: {integrity: sha512-SPMcGZuP71Fj8btCGtlBnv8h8DAbJn8EQfLzXs9oo6NGFFLVjNGiFpqGfgtUV6DLWCuaRyEFeViO7wZow/vKGQ==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: glibc
+
+  sass-embedded-linux-arm@1.90.0:
+    resolution: {integrity: sha512-FeBxI5Q2HvM3CCadcEcQgvWbDPVs2YEF0PZ87fbAVTCG8dV+iNnQreSz7GRJroknpvbRhm5t2gedvcgmTnPb2Q==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: glibc
+
+  sass-embedded-linux-musl-arm64@1.90.0:
+    resolution: {integrity: sha512-xLH7+PFq763MoEm3vI7hQk5E+nStiLWbijHEYW/tEtCbcQIphgzSkDItEezxXew3dU4EJ1jqrBUySPdoXFLqWA==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm64]
+    os: [linux]
+    libc: musl
+
+  sass-embedded-linux-musl-arm@1.90.0:
+    resolution: {integrity: sha512-EB2z0fUXdUdvSoddf4DzdZQkD/xyreD72gwAi8YScgUvR4HMXI7bLcK/n78Rft6OnqvV8090hjC8FsLDo3x5xQ==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm]
+    os: [linux]
+    libc: musl
+
+  sass-embedded-linux-musl-riscv64@1.90.0:
+    resolution: {integrity: sha512-L21UkOgnSrD+ERF+jo1IWneGv40t0ap9+3cI+wZWYhQS5MkxponhT9QaNU57JEDJwB9mOl01LVw14opz4SN+VQ==}
+    engines: {node: '>=14.0.0'}
+    cpu: [riscv64]
+    os: [linux]
+    libc: musl
+
+  sass-embedded-linux-musl-x64@1.90.0:
+    resolution: {integrity: sha512-NeAycQlsdhFdnIeSmRmScYUyCd+uE+x15NLFunbF8M0PgCKurrUhaxgGKSuBbaK56FpxarKOHCqcOrWbemIGzQ==}
+    engines: {node: '>=14.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: musl
+
+  sass-embedded-linux-riscv64@1.90.0:
+    resolution: {integrity: sha512-lJopaQhW8S+kaQ61vMqq3c+bOurcf9RdZf2EmzQYpc2y1vT5cWfRNrRkbAgO/23IQxsk/fq3UIUnsjnyQmi6MA==}
+    engines: {node: '>=14.0.0'}
+    cpu: [riscv64]
+    os: [linux]
+    libc: glibc
+
+  sass-embedded-linux-x64@1.90.0:
+    resolution: {integrity: sha512-Cc061gBfMPwH9rN7neQaH36cvOQC+dFMSGIeX5qUOhrEL4Ng51iqBV6aI4RIB1jCFGth6eDydVRN1VdV9qom8A==}
+    engines: {node: '>=14.0.0'}
+    cpu: [x64]
+    os: [linux]
+    libc: glibc
+
+  sass-embedded-unknown-all@1.90.0:
+    resolution: {integrity: sha512-DBGzHVCJDqtjTHZFohush9YTxd4ZxhIygIRTNRXnA0359woF9Z8AS7/YxfzwkqrTX5durSJa6ZamGFYVLoRphQ==}
+    os: ['!android', '!darwin', '!linux', '!win32']
+
+  sass-embedded-win32-arm64@1.90.0:
+    resolution: {integrity: sha512-c3/vL/CATnaW3x/6kcNbCROEOUU7zvJpIURp7M9664GJj08/gLPRWKNruw0OkAPQ3C5TTQz7+/fQWEpRA6qmvA==}
+    engines: {node: '>=14.0.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  sass-embedded-win32-x64@1.90.0:
+    resolution: {integrity: sha512-PFwdW7AYtCkwi3NfWFeexvIZEJ0nuShp8Bjjc3px756+18yYwBWa78F4TGdIQmJfpYKBhgkVjFOctwq+NCHntA==}
+    engines: {node: '>=14.0.0'}
+    cpu: [x64]
+    os: [win32]
+
+  sass-embedded@1.90.0:
+    resolution: {integrity: sha512-XP1EltyLLfuU5FsGVjSz8PcT925oA3rDnJTWOEBHR42k62ZEbKTcZ4gVlFwKi0Ggzi5E8v1K2BplD8ELHwusYg==}
+    engines: {node: '>=16.0.0'}
+    hasBin: true
+
+  sass@1.90.0:
+    resolution: {integrity: sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  saxes@6.0.0:
+    resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+    engines: {node: '>=v12.22.7'}
+
+  scslre@0.3.0:
+    resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
+    engines: {node: ^14.0.0 || >=16.0.0}
+
+  scule@1.3.0:
+    resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
+
+  seemly@0.3.10:
+    resolution: {integrity: sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==}
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  semver@7.7.2:
+    resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+
+  shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+
+  shell-quote@1.8.3:
+    resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
+    engines: {node: '>= 0.4'}
+
+  siginfo@2.0.0:
+    resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+  signal-exit@3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+  signal-exit@4.1.0:
+    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+    engines: {node: '>=14'}
+
+  simple-swizzle@0.2.2:
+    resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+
+  sirv@3.0.1:
+    resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
+    engines: {node: '>=18'}
+
+  sisteransi@1.0.5:
+    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  source-map-support@0.5.21:
+    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+  source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  spdx-exceptions@2.5.0:
+    resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==}
+
+  spdx-expression-parse@4.0.0:
+    resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==}
+
+  spdx-license-ids@3.0.22:
+    resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==}
+
+  speakingurl@14.0.1:
+    resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+    engines: {node: '>=0.10.0'}
+
+  stackback@0.0.2:
+    resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+  std-env@3.9.0:
+    resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
+
+  string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+
+  string-width@5.1.2:
+    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+    engines: {node: '>=12'}
+
+  strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+
+  strip-ansi@7.1.0:
+    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+    engines: {node: '>=12'}
+
+  strip-final-newline@2.0.0:
+    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+    engines: {node: '>=6'}
+
+  strip-final-newline@4.0.0:
+    resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
+    engines: {node: '>=18'}
+
+  strip-indent@4.0.0:
+    resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
+    engines: {node: '>=12'}
+
+  strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+
+  strip-literal@3.0.0:
+    resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
+
+  structured-clone-es@1.0.0:
+    resolution: {integrity: sha512-FL8EeKFFyNQv5cMnXI31CIMCsFarSVI2bF0U0ImeNE3g/F1IvJQyqzOXxPBRXiwQfyBTlbNe88jh1jFW0O/jiQ==}
+
+  superjson@2.2.2:
+    resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
+    engines: {node: '>=16'}
+
+  supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+
+  supports-color@8.1.1:
+    resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+    engines: {node: '>=10'}
+
+  svg-path-parser@1.1.0:
+    resolution: {integrity: sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A==}
+
+  symbol-tree@3.2.4:
+    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+  sync-child-process@1.0.2:
+    resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==}
+    engines: {node: '>=16.0.0'}
+
+  sync-message-port@1.1.3:
+    resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
+    engines: {node: '>=16.0.0'}
+
+  synckit@0.11.11:
+    resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+
+  tapable@2.2.2:
+    resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
+    engines: {node: '>=6'}
+
+  terser@5.43.1:
+    resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  tinybench@2.9.0:
+    resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+  tinyexec@0.3.2:
+    resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
+  tinyexec@1.0.1:
+    resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
+
+  tinyglobby@0.2.14:
+    resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
+    engines: {node: '>=12.0.0'}
+
+  tinypool@1.1.1:
+    resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+
+  tinyrainbow@2.0.0:
+    resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
+    engines: {node: '>=14.0.0'}
+
+  tinyspy@4.0.3:
+    resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==}
+    engines: {node: '>=14.0.0'}
+
+  tldts-core@6.1.86:
+    resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
+
+  tldts@6.1.86:
+    resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
+    hasBin: true
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  toml-eslint-parser@0.10.0:
+    resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  totalist@3.0.1:
+    resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+    engines: {node: '>=6'}
+
+  tough-cookie@5.1.2:
+    resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
+    engines: {node: '>=16'}
+
+  tr46@5.1.1:
+    resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
+    engines: {node: '>=18'}
+
+  treemate@0.3.11:
+    resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==}
+
+  ts-api-utils@2.1.0:
+    resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+    engines: {node: '>=18.12'}
+    peerDependencies:
+      typescript: '>=4.8.4'
+
+  ts-declaration-location@1.0.7:
+    resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==}
+    peerDependencies:
+      typescript: '>=4.0.0'
+
+  ts-macro@0.3.2:
+    resolution: {integrity: sha512-vyA6HztBoUayJAdE7G1tdhu54GG5PzgMja2dTLej6HMkSMAKL8twI03omj5QiwNiHw2UFP/OG/uqO2EgdJDTRw==}
+
+  ts-morph@26.0.0:
+    resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==}
+
+  tslib@2.8.1:
+    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+  type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+
+  type-fest@0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+
+  typescript@5.8.3:
+    resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  ufo@1.6.1:
+    resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
+
+  unconfig@7.3.2:
+    resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==}
+
+  uncrypto@0.1.3:
+    resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
+
+  undici-types@6.21.0:
+    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+  unicorn-magic@0.3.0:
+    resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
+    engines: {node: '>=18'}
+
+  unimport@5.2.0:
+    resolution: {integrity: sha512-bTuAMMOOqIAyjV4i4UH7P07pO+EsVxmhOzQ2YJ290J6mkLUdozNhb5I/YoOEheeNADC03ent3Qj07X0fWfUpmw==}
+    engines: {node: '>=18.12.0'}
+
+  unist-util-is@6.0.0:
+    resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+
+  unist-util-stringify-position@4.0.0:
+    resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+  unist-util-visit-parents@6.0.1:
+    resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+
+  unist-util-visit@5.0.0:
+    resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
+  universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+
+  unocss@66.4.1:
+    resolution: {integrity: sha512-9nZRafw74usdYIHnfBzCtYlPZUjmLOZZ4fxcohMaQtQon+A9P5KiJoaX+9Z2PxVqnArB3drQ1KiRxFPMHJN3+A==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@unocss/webpack': 66.4.1
+      vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+    peerDependenciesMeta:
+      '@unocss/webpack':
+        optional: true
+      vite:
+        optional: true
+
+  unplugin-auto-import@20.0.0:
+    resolution: {integrity: sha512-YAqD07gQa5PpGlhLFQ9rWDtGa42nBR2oNMudnX7G5P8XmkQx3EPL7sKNQroTdt5ehwks55NHy0dzxwwA3xtqQg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@nuxt/kit': ^4.0.0
+      '@vueuse/core': '*'
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+      '@vueuse/core':
+        optional: true
+
+  unplugin-combine@2.1.0:
+    resolution: {integrity: sha512-pxDpLICZMVUXAjJDAJyY4WCA4wT2pCCZ6CAPX0nEGaCh/lfE0wZcou8EC8Uq6D6ZA34IWIwYvcfI0CwYTeCquA==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      '@rspack/core': '*'
+      esbuild: '>=0.13'
+      rolldown: '*'
+      rollup: ^3.2.0 || ^4.0.0
+      unplugin: ^1.0.0 || ^2.0.0
+      vite: ^2.3.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+      webpack: 4 || 5
+    peerDependenciesMeta:
+      '@rspack/core':
+        optional: true
+      esbuild:
+        optional: true
+      rolldown:
+        optional: true
+      rollup:
+        optional: true
+      unplugin:
+        optional: true
+      vite:
+        optional: true
+      webpack:
+        optional: true
+
+  unplugin-utils@0.2.5:
+    resolution: {integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==}
+    engines: {node: '>=18.12.0'}
+
+  unplugin-vue-components@28.8.0:
+    resolution: {integrity: sha512-2Q6ZongpoQzuXDK0ZsVzMoshH0MWZQ1pzVL538G7oIDKRTVzHjppBDS8aB99SADGHN3lpGU7frraCG6yWNoL5Q==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@babel/parser': ^7.15.8
+      '@nuxt/kit': ^3.2.2 || ^4.0.0
+      vue: 2 || 3
+    peerDependenciesMeta:
+      '@babel/parser':
+        optional: true
+      '@nuxt/kit':
+        optional: true
+
+  unplugin-vue-define-options@3.0.0-beta.21:
+    resolution: {integrity: sha512-lwGAB22LiYhypVDtf7/M1qaGmasYtoUR66LPgrCrSu2pmTpeVJtxh8MNf+kM/+tNajMZLlatu/2Yds3Z5ajUhA==}
+    engines: {node: '>=20.18.0'}
+
+  unplugin@2.3.5:
+    resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==}
+    engines: {node: '>=18.12.0'}
+
+  unstorage@1.16.1:
+    resolution: {integrity: sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==}
+    peerDependencies:
+      '@azure/app-configuration': ^1.8.0
+      '@azure/cosmos': ^4.2.0
+      '@azure/data-tables': ^13.3.0
+      '@azure/identity': ^4.6.0
+      '@azure/keyvault-secrets': ^4.9.0
+      '@azure/storage-blob': ^12.26.0
+      '@capacitor/preferences': ^6.0.3 || ^7.0.0
+      '@deno/kv': '>=0.9.0'
+      '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0
+      '@planetscale/database': ^1.19.0
+      '@upstash/redis': ^1.34.3
+      '@vercel/blob': '>=0.27.1'
+      '@vercel/kv': ^1.0.1
+      aws4fetch: ^1.0.20
+      db0: '>=0.2.1'
+      idb-keyval: ^6.2.1
+      ioredis: ^5.4.2
+      uploadthing: ^7.4.4
+    peerDependenciesMeta:
+      '@azure/app-configuration':
+        optional: true
+      '@azure/cosmos':
+        optional: true
+      '@azure/data-tables':
+        optional: true
+      '@azure/identity':
+        optional: true
+      '@azure/keyvault-secrets':
+        optional: true
+      '@azure/storage-blob':
+        optional: true
+      '@capacitor/preferences':
+        optional: true
+      '@deno/kv':
+        optional: true
+      '@netlify/blobs':
+        optional: true
+      '@planetscale/database':
+        optional: true
+      '@upstash/redis':
+        optional: true
+      '@vercel/blob':
+        optional: true
+      '@vercel/kv':
+        optional: true
+      aws4fetch:
+        optional: true
+      db0:
+        optional: true
+      idb-keyval:
+        optional: true
+      ioredis:
+        optional: true
+      uploadthing:
+        optional: true
+
+  update-browserslist-db@1.1.3:
+    resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  uuid@11.1.0:
+    resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
+    hasBin: true
+
+  varint@6.0.0:
+    resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
+
+  vdirs@0.1.8:
+    resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==}
+    peerDependencies:
+      vue: ^3.0.11
+
+  vite-dev-rpc@1.1.0:
+    resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==}
+    peerDependencies:
+      vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0
+
+  vite-hot-client@2.1.0:
+    resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==}
+    peerDependencies:
+      vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite-node@3.2.4:
+    resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
+    engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+    hasBin: true
+
+  vite-plugin-compression@0.5.1:
+    resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
+    peerDependencies:
+      vite: '>=2.0.0'
+
+  vite-plugin-html@3.2.2:
+    resolution: {integrity: sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==}
+    peerDependencies:
+      vite: '>=2.0.0'
+
+  vite-plugin-inspect@11.3.2:
+    resolution: {integrity: sha512-nzwvyFQg58XSMAmKVLr2uekAxNYvAbz1lyPmCAFVIBncCgN9S/HPM+2UM9Q9cvc4JEbC5ZBgwLAdaE2onmQuKg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@nuxt/kit': '*'
+      vite: ^6.0.0 || ^7.0.0-0
+    peerDependenciesMeta:
+      '@nuxt/kit':
+        optional: true
+
+  vite-plugin-vue-devtools@7.7.7:
+    resolution: {integrity: sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==}
+    engines: {node: '>=v14.21.3'}
+    peerDependencies:
+      vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite-plugin-vue-inspector@5.3.2:
+    resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==}
+    peerDependencies:
+      vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+  vite@7.1.0:
+    resolution: {integrity: sha512-3jdAy3NhBJYsa/lCFcnRfbK4kNkO/bhijFCnv5ByUQk/eekYagoV2yQSISUrhpV+5JiY5hmwOh7jNnQ68dFMuQ==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^20.19.0 || >=22.12.0
+      jiti: '>=1.21.0'
+      less: ^4.0.0
+      lightningcss: ^1.21.0
+      sass: ^1.70.0
+      sass-embedded: ^1.70.0
+      stylus: '>=0.54.8'
+      sugarss: ^5.0.0
+      terser: ^5.16.0
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      jiti:
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+  vitest@3.2.4:
+    resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
+    engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+    hasBin: true
+    peerDependencies:
+      '@edge-runtime/vm': '*'
+      '@types/debug': ^4.1.12
+      '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+      '@vitest/browser': 3.2.4
+      '@vitest/ui': 3.2.4
+      happy-dom: '*'
+      jsdom: '*'
+    peerDependenciesMeta:
+      '@edge-runtime/vm':
+        optional: true
+      '@types/debug':
+        optional: true
+      '@types/node':
+        optional: true
+      '@vitest/browser':
+        optional: true
+      '@vitest/ui':
+        optional: true
+      happy-dom:
+        optional: true
+      jsdom:
+        optional: true
+
+  vooks@0.2.12:
+    resolution: {integrity: sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==}
+    peerDependencies:
+      vue: ^3.0.0
+
+  vscode-jsonrpc@8.2.0:
+    resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
+    engines: {node: '>=14.0.0'}
+
+  vscode-languageserver-protocol@3.17.5:
+    resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
+
+  vscode-languageserver-textdocument@1.0.12:
+    resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==}
+
+  vscode-languageserver-types@3.17.5:
+    resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
+
+  vscode-languageserver@9.0.1:
+    resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
+    hasBin: true
+
+  vscode-uri@3.1.0:
+    resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+  vue-component-type-helpers@2.2.12:
+    resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
+
+  vue-draggable-plus@0.6.0:
+    resolution: {integrity: sha512-G5TSfHrt9tX9EjdG49InoFJbt2NYk0h3kgjgKxkFWr3ulIUays0oFObr5KZ8qzD4+QnhtALiRwIqY6qul4egqw==}
+    peerDependencies:
+      '@types/sortablejs': ^1.15.0
+      '@vue/composition-api': '*'
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
+  vue-eslint-parser@10.2.0:
+    resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0
+
+  vue-flow-layout@0.2.0:
+    resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==}
+
+  vue-macros@3.0.0-beta.21:
+    resolution: {integrity: sha512-EYofxb1wLMkb9pUFqiMhTmZvAWZEsSowECKNOo42P7alA+uwalP8BKBYul/BuGIRZSjI8ejvSCxMddXYDPS3sw==}
+    engines: {node: '>=20.18.0'}
+    peerDependencies:
+      vue: ^2.7.0 || ^3.2.25
+
+  vue-router@4.5.1:
+    resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
+    peerDependencies:
+      vue: ^3.2.0
+
+  vue-tsc@3.0.5:
+    resolution: {integrity: sha512-PsTFN9lo1HJCrZw9NoqjYcAbYDXY0cOKyuW2E7naX5jcaVyWpqEsZOHN9Dws5890E8e5SDAD4L4Zam3dxG3/Cw==}
+    hasBin: true
+    peerDependencies:
+      typescript: '>=5.0.0'
+
+  vue-vine-tsc@1.7.0:
+    resolution: {integrity: sha512-Dufo7z/RKFYDzNP4rVE2+36Iy7OnShg3Wl3lze8P7iCKG4FP6fOTFG+TWji4jhycKmlfGji7MGT7eC+A+ZCu6g==}
+    hasBin: true
+
+  vue@3.5.18:
+    resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  vueuc@0.4.64:
+    resolution: {integrity: sha512-wlJQj7fIwKK2pOEoOq4Aro8JdPOGpX8aWQhV8YkTW9OgWD2uj2O8ANzvSsIGjx7LTOc7QbS7sXdxHi6XvRnHPA==}
+    peerDependencies:
+      vue: ^3.0.11
+
+  w3c-xmlserializer@5.0.0:
+    resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+    engines: {node: '>=18'}
+
+  webidl-conversions@7.0.0:
+    resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+    engines: {node: '>=12'}
+
+  webpack-virtual-modules@0.6.2:
+    resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+  whatwg-encoding@3.1.1:
+    resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+    engines: {node: '>=18'}
+
+  whatwg-mimetype@4.0.0:
+    resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+    engines: {node: '>=18'}
+
+  whatwg-url@14.2.0:
+    resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
+    engines: {node: '>=18'}
+
+  which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+
+  which@4.0.0:
+    resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==}
+    engines: {node: ^16.13.0 || >=18.0.0}
+    hasBin: true
+
+  which@5.0.0:
+    resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
+  why-is-node-running@2.3.0:
+    resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+    engines: {node: '>=8'}
+    hasBin: true
+
+  word-wrap@1.2.5:
+    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+    engines: {node: '>=0.10.0'}
+
+  wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+
+  wrap-ansi@8.1.0:
+    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+    engines: {node: '>=12'}
+
+  ws@8.18.3:
+    resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: '>=5.0.2'
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+
+  wsl-utils@0.1.0:
+    resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
+    engines: {node: '>=18'}
+
+  xml-name-validator@4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+
+  xml-name-validator@5.0.0:
+    resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+    engines: {node: '>=18'}
+
+  xmlchars@2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yaml-eslint-parser@1.3.0:
+    resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+
+  yaml@2.8.1:
+    resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
+    engines: {node: '>= 14.6'}
+    hasBin: true
+
+  yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+
+  yocto-queue@1.2.1:
+    resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==}
+    engines: {node: '>=12.20'}
+
+  yoctocolors@2.1.1:
+    resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
+    engines: {node: '>=18'}
+
+  zwitch@2.0.4:
+    resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+  '@ampproject/remapping@2.3.0':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@antfu/eslint-config@5.2.0(@vue/compiler-sfc@3.5.18)(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4)':
+    dependencies:
+      '@antfu/install-pkg': 1.1.0
+      '@clack/prompts': 0.11.0
+      '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.32.0(jiti@2.5.1))
+      '@eslint/markdown': 7.1.0
+      '@stylistic/eslint-plugin': 5.2.2(eslint@9.32.0(jiti@2.5.1))
+      '@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      '@vitest/eslint-plugin': 1.3.4(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4)
+      ansis: 4.1.0
+      cac: 6.7.14
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-config-flat-gitignore: 2.1.0(eslint@9.32.0(jiti@2.5.1))
+      eslint-flat-config-utils: 2.1.1
+      eslint-merge-processors: 2.0.0(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-antfu: 3.1.1(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-command: 3.3.1(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-import-lite: 0.3.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      eslint-plugin-jsdoc: 52.0.4(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-jsonc: 2.20.1(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-n: 17.21.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      eslint-plugin-no-only-tests: 3.3.0
+      eslint-plugin-perfectionist: 4.15.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      eslint-plugin-pnpm: 1.1.0(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-regexp: 2.9.1(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-toml: 0.12.0(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-unicorn: 60.0.0(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-unused-imports: 4.1.4(@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-vue: 10.4.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.32.0(jiti@2.5.1)))
+      eslint-plugin-yml: 1.18.0(eslint@9.32.0(jiti@2.5.1))
+      eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.18)(eslint@9.32.0(jiti@2.5.1))
+      globals: 16.3.0
+      jsonc-eslint-parser: 2.4.0
+      local-pkg: 1.1.1
+      parse-gitignore: 2.0.0
+      toml-eslint-parser: 0.10.0
+      vue-eslint-parser: 10.2.0(eslint@9.32.0(jiti@2.5.1))
+      yaml-eslint-parser: 1.3.0
+    transitivePeerDependencies:
+      - '@eslint/json'
+      - '@vue/compiler-sfc'
+      - supports-color
+      - typescript
+      - vitest
+
+  '@antfu/install-pkg@1.1.0':
+    dependencies:
+      package-manager-detector: 1.3.0
+      tinyexec: 1.0.1
+
+  '@antfu/utils@8.1.1': {}
+
+  '@antv/component@2.1.5':
+    dependencies:
+      '@antv/g': 6.1.28
+      '@antv/scale': 0.4.16
+      '@antv/util': 3.3.11
+      svg-path-parser: 1.1.0
+
+  '@antv/coord@0.4.7':
+    dependencies:
+      '@antv/scale': 0.4.16
+      '@antv/util': 2.0.17
+      gl-matrix: 3.4.3
+
+  '@antv/event-emitter@0.1.3': {}
+
+  '@antv/expr@1.0.2': {}
+
+  '@antv/g-camera-api@2.0.41':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-canvas@2.0.48':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/g-plugin-canvas-path-generator': 2.1.22
+      '@antv/g-plugin-canvas-picker': 2.1.27
+      '@antv/g-plugin-canvas-renderer': 2.3.3
+      '@antv/g-plugin-dom-interaction': 2.1.27
+      '@antv/g-plugin-html-renderer': 2.1.27
+      '@antv/g-plugin-image-loader': 2.1.26
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      tslib: 2.8.1
+
+  '@antv/g-device-api@1.6.13':
+    dependencies:
+      '@antv/util': 3.3.11
+      '@webgpu/types': 0.1.64
+      eventemitter3: 5.0.1
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-dom-mutation-observer-api@2.0.38':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@babel/runtime': 7.28.2
+
+  '@antv/g-lite@2.3.2':
+    dependencies:
+      '@antv/g-math': 3.0.1
+      '@antv/util': 3.3.11
+      '@antv/vendor': 1.0.11
+      '@babel/runtime': 7.28.2
+      eventemitter3: 5.0.1
+      gl-matrix: 3.4.3
+      rbush: 3.0.1
+      tslib: 2.8.1
+
+  '@antv/g-math@3.0.1':
+    dependencies:
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-plugin-canvas-path-generator@2.1.22':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/g-math': 3.0.1
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      tslib: 2.8.1
+
+  '@antv/g-plugin-canvas-picker@2.1.27':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/g-math': 3.0.1
+      '@antv/g-plugin-canvas-path-generator': 2.1.22
+      '@antv/g-plugin-canvas-renderer': 2.3.3
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-plugin-canvas-renderer@2.3.3':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/g-math': 3.0.1
+      '@antv/g-plugin-canvas-path-generator': 2.1.22
+      '@antv/g-plugin-image-loader': 2.1.26
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-plugin-device-renderer@2.3.3':
+    dependencies:
+      '@antv/g-device-api': 1.6.13
+      '@antv/g-lite': 2.3.2
+      '@antv/g-math': 3.0.1
+      '@antv/g-plugin-image-loader': 2.1.26
+      '@antv/g-shader-components': 2.0.0
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      '@webgpu/types': 0.1.64
+      earcut: 2.2.4
+      eventemitter3: 5.0.1
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-plugin-dom-interaction@2.1.27':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@babel/runtime': 7.28.2
+      tslib: 2.8.1
+
+  '@antv/g-plugin-dragndrop@2.0.38':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      tslib: 2.8.1
+
+  '@antv/g-plugin-html-renderer@2.1.27':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-plugin-image-loader@2.1.26':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g-shader-components@2.0.0': {}
+
+  '@antv/g-web-animations-api@2.1.28':
+    dependencies:
+      '@antv/g-lite': 2.3.2
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      tslib: 2.8.1
+
+  '@antv/g-webgl@2.0.52':
+    dependencies:
+      '@antv/g-device-api': 1.6.13
+      '@antv/g-lite': 2.3.2
+      '@antv/g-plugin-device-renderer': 2.3.3
+      '@antv/g-plugin-dom-interaction': 2.1.27
+      '@antv/g-plugin-html-renderer': 2.1.27
+      '@antv/g-plugin-image-loader': 2.1.26
+      '@antv/util': 3.3.11
+      '@babel/runtime': 7.28.2
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/g2@5.3.5':
+    dependencies:
+      '@antv/component': 2.1.5
+      '@antv/coord': 0.4.7
+      '@antv/event-emitter': 0.1.3
+      '@antv/expr': 1.0.2
+      '@antv/g': 6.1.28
+      '@antv/g-canvas': 2.0.48
+      '@antv/g-plugin-dragndrop': 2.0.38
+      '@antv/scale': 0.4.16
+      '@antv/util': 3.3.11
+      '@antv/vendor': 1.0.11
+      flru: 1.0.2
+      pdfast: 0.2.0
+
+  '@antv/g@6.1.28':
+    dependencies:
+      '@antv/g-camera-api': 2.0.41
+      '@antv/g-dom-mutation-observer-api': 2.0.38
+      '@antv/g-lite': 2.3.2
+      '@antv/g-web-animations-api': 2.1.28
+      '@babel/runtime': 7.28.2
+
+  '@antv/scale@0.4.16':
+    dependencies:
+      '@antv/util': 3.3.11
+      color-string: 1.9.1
+      fecha: 4.2.3
+
+  '@antv/util@2.0.17':
+    dependencies:
+      csstype: 3.1.3
+      tslib: 2.8.1
+
+  '@antv/util@3.3.11':
+    dependencies:
+      fast-deep-equal: 3.1.3
+      gl-matrix: 3.4.3
+      tslib: 2.8.1
+
+  '@antv/vendor@1.0.11':
+    dependencies:
+      '@types/d3-array': 3.2.1
+      '@types/d3-color': 3.1.3
+      '@types/d3-dispatch': 3.0.7
+      '@types/d3-dsv': 3.0.7
+      '@types/d3-ease': 3.0.2
+      '@types/d3-fetch': 3.0.7
+      '@types/d3-force': 3.0.10
+      '@types/d3-format': 3.0.4
+      '@types/d3-geo': 3.1.0
+      '@types/d3-hierarchy': 3.1.7
+      '@types/d3-interpolate': 3.0.4
+      '@types/d3-path': 3.1.1
+      '@types/d3-quadtree': 3.0.6
+      '@types/d3-random': 3.0.3
+      '@types/d3-scale': 4.0.9
+      '@types/d3-scale-chromatic': 3.1.0
+      '@types/d3-shape': 3.1.7
+      '@types/d3-time': 3.0.4
+      '@types/d3-timer': 3.0.2
+      d3-array: 3.2.4
+      d3-color: 3.1.0
+      d3-dispatch: 3.0.1
+      d3-dsv: 3.0.1
+      d3-ease: 3.0.1
+      d3-fetch: 3.0.1
+      d3-force: 3.0.0
+      d3-force-3d: 3.0.6
+      d3-format: 3.1.0
+      d3-geo: 3.1.1
+      d3-geo-projection: 4.0.0
+      d3-hierarchy: 3.1.2
+      d3-interpolate: 3.0.1
+      d3-path: 3.1.0
+      d3-quadtree: 3.0.1
+      d3-random: 3.0.1
+      d3-regression: 1.3.10
+      d3-scale: 4.0.2
+      d3-scale-chromatic: 3.1.0
+      d3-shape: 3.2.0
+      d3-time: 3.1.0
+      d3-timer: 3.0.1
+
+  '@asamuzakjp/css-color@3.2.0':
+    dependencies:
+      '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+      '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+      '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+      '@csstools/css-tokenizer': 3.0.4
+      lru-cache: 10.4.3
+
+  '@babel/code-frame@7.27.1':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.27.1
+      js-tokens: 4.0.0
+      picocolors: 1.1.1
+
+  '@babel/compat-data@7.28.0': {}
+
+  '@babel/core@7.28.0':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.27.1
+      '@babel/generator': 7.28.0
+      '@babel/helper-compilation-targets': 7.27.2
+      '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0)
+      '@babel/helpers': 7.28.2
+      '@babel/parser': 7.28.0
+      '@babel/template': 7.27.2
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.2
+      convert-source-map: 2.0.0
+      debug: 4.4.1
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/generator@7.28.0':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@babel/types': 7.28.2
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+      jsesc: 3.1.0
+
+  '@babel/helper-annotate-as-pure@7.27.3':
+    dependencies:
+      '@babel/types': 7.28.2
+
+  '@babel/helper-compilation-targets@7.27.2':
+    dependencies:
+      '@babel/compat-data': 7.28.0
+      '@babel/helper-validator-option': 7.27.1
+      browserslist: 4.25.1
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
+  '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-annotate-as-pure': 7.27.3
+      '@babel/helper-member-expression-to-functions': 7.27.1
+      '@babel/helper-optimise-call-expression': 7.27.1
+      '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+      '@babel/traverse': 7.28.0
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-globals@7.28.0': {}
+
+  '@babel/helper-member-expression-to-functions@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-imports@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+      '@babel/traverse': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-optimise-call-expression@7.27.1':
+    dependencies:
+      '@babel/types': 7.28.2
+
+  '@babel/helper-plugin-utils@7.27.1': {}
+
+  '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-member-expression-to-functions': 7.27.1
+      '@babel/helper-optimise-call-expression': 7.27.1
+      '@babel/traverse': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+    dependencies:
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-string-parser@7.27.1': {}
+
+  '@babel/helper-validator-identifier@7.27.1': {}
+
+  '@babel/helper-validator-option@7.27.1': {}
+
+  '@babel/helpers@7.28.2':
+    dependencies:
+      '@babel/template': 7.27.2
+      '@babel/types': 7.28.2
+
+  '@babel/parser@7.28.0':
+    dependencies:
+      '@babel/types': 7.28.2
+
+  '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-plugin-utils': 7.27.1
+
+  '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/helper-annotate-as-pure': 7.27.3
+      '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0)
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+      '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/runtime@7.28.2': {}
+
+  '@babel/template@7.27.2':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/parser': 7.28.0
+      '@babel/types': 7.28.2
+
+  '@babel/traverse@7.28.0':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/generator': 7.28.0
+      '@babel/helper-globals': 7.28.0
+      '@babel/parser': 7.28.0
+      '@babel/template': 7.27.2
+      '@babel/types': 7.28.2
+      debug: 4.4.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.28.2':
+    dependencies:
+      '@babel/helper-string-parser': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+
+  '@bufbuild/protobuf@2.6.3': {}
+
+  '@clack/core@0.5.0':
+    dependencies:
+      picocolors: 1.1.1
+      sisteransi: 1.0.5
+
+  '@clack/prompts@0.11.0':
+    dependencies:
+      '@clack/core': 0.5.0
+      picocolors: 1.1.1
+      sisteransi: 1.0.5
+
+  '@css-render/plugin-bem@0.15.14(css-render@0.15.14)':
+    dependencies:
+      css-render: 0.15.14
+
+  '@css-render/vue3-ssr@0.15.14(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@csstools/color-helpers@5.0.2': {}
+
+  '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+    dependencies:
+      '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+      '@csstools/css-tokenizer': 3.0.4
+
+  '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+    dependencies:
+      '@csstools/color-helpers': 5.0.2
+      '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+      '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+      '@csstools/css-tokenizer': 3.0.4
+
+  '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+    dependencies:
+      '@csstools/css-tokenizer': 3.0.4
+
+  '@csstools/css-tokenizer@3.0.4': {}
+
+  '@dotenvx/dotenvx@1.48.4':
+    dependencies:
+      commander: 11.1.0
+      dotenv: 17.2.1
+      eciesjs: 0.4.15
+      execa: 5.1.1
+      fdir: 6.4.6(picomatch@4.0.3)
+      ignore: 5.3.2
+      object-treeify: 1.1.33
+      picomatch: 4.0.3
+      which: 4.0.0
+
+  '@ecies/ciphers@0.2.4(@noble/ciphers@1.3.0)':
+    dependencies:
+      '@noble/ciphers': 1.3.0
+
+  '@emnapi/core@1.4.5':
+    dependencies:
+      '@emnapi/wasi-threads': 1.0.4
+      tslib: 2.8.1
+    optional: true
+
+  '@emnapi/runtime@1.4.5':
+    dependencies:
+      tslib: 2.8.1
+    optional: true
+
+  '@emnapi/wasi-threads@1.0.4':
+    dependencies:
+      tslib: 2.8.1
+    optional: true
+
+  '@emotion/hash@0.8.0': {}
+
+  '@es-joy/jsdoccomment@0.50.2':
+    dependencies:
+      '@types/estree': 1.0.8
+      '@typescript-eslint/types': 8.39.0
+      comment-parser: 1.4.1
+      esquery: 1.6.0
+      jsdoc-type-pratt-parser: 4.1.0
+
+  '@es-joy/jsdoccomment@0.52.0':
+    dependencies:
+      '@types/estree': 1.0.8
+      '@typescript-eslint/types': 8.39.0
+      comment-parser: 1.4.1
+      esquery: 1.6.0
+      jsdoc-type-pratt-parser: 4.1.0
+
+  '@esbuild/aix-ppc64@0.25.8':
+    optional: true
+
+  '@esbuild/android-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/android-arm@0.25.8':
+    optional: true
+
+  '@esbuild/android-x64@0.25.8':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/darwin-x64@0.25.8':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.25.8':
+    optional: true
+
+  '@esbuild/linux-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/linux-arm@0.25.8':
+    optional: true
+
+  '@esbuild/linux-ia32@0.25.8':
+    optional: true
+
+  '@esbuild/linux-loong64@0.25.8':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.25.8':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.25.8':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.25.8':
+    optional: true
+
+  '@esbuild/linux-s390x@0.25.8':
+    optional: true
+
+  '@esbuild/linux-x64@0.25.8':
+    optional: true
+
+  '@esbuild/netbsd-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.25.8':
+    optional: true
+
+  '@esbuild/openbsd-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.25.8':
+    optional: true
+
+  '@esbuild/openharmony-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/sunos-x64@0.25.8':
+    optional: true
+
+  '@esbuild/win32-arm64@0.25.8':
+    optional: true
+
+  '@esbuild/win32-ia32@0.25.8':
+    optional: true
+
+  '@esbuild/win32-x64@0.25.8':
+    optional: true
+
+  '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.32.0(jiti@2.5.1))':
+    dependencies:
+      escape-string-regexp: 4.0.0
+      eslint: 9.32.0(jiti@2.5.1)
+      ignore: 5.3.2
+
+  '@eslint-community/eslint-utils@4.7.0(eslint@9.32.0(jiti@2.5.1))':
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-visitor-keys: 3.4.3
+
+  '@eslint-community/regexpp@4.12.1': {}
+
+  '@eslint/compat@1.3.1(eslint@9.32.0(jiti@2.5.1))':
+    optionalDependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+
+  '@eslint/config-array@0.21.0':
+    dependencies:
+      '@eslint/object-schema': 2.1.6
+      debug: 4.4.1
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/config-helpers@0.3.0': {}
+
+  '@eslint/core@0.15.1':
+    dependencies:
+      '@types/json-schema': 7.0.15
+
+  '@eslint/eslintrc@3.3.1':
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.4.1
+      espree: 10.4.0
+      globals: 14.0.0
+      ignore: 5.3.2
+      import-fresh: 3.3.1
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/js@9.32.0': {}
+
+  '@eslint/markdown@7.1.0':
+    dependencies:
+      '@eslint/core': 0.15.1
+      '@eslint/plugin-kit': 0.3.4
+      github-slugger: 2.0.0
+      mdast-util-from-markdown: 2.0.2
+      mdast-util-frontmatter: 2.0.1
+      mdast-util-gfm: 3.1.0
+      micromark-extension-frontmatter: 2.0.0
+      micromark-extension-gfm: 3.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/object-schema@2.1.6': {}
+
+  '@eslint/plugin-kit@0.3.4':
+    dependencies:
+      '@eslint/core': 0.15.1
+      levn: 0.4.1
+
+  '@humanfs/core@0.19.1': {}
+
+  '@humanfs/node@0.16.6':
+    dependencies:
+      '@humanfs/core': 0.19.1
+      '@humanwhocodes/retry': 0.3.1
+
+  '@humanwhocodes/module-importer@1.0.1': {}
+
+  '@humanwhocodes/retry@0.3.1': {}
+
+  '@humanwhocodes/retry@0.4.3': {}
+
+  '@iconify-json/carbon@1.2.13':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/fluent@1.2.28':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/hugeicons@1.2.6':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/mage@1.2.2':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/material-symbols@1.2.30':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/mdi@1.2.3':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/ph@1.2.2':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/pixelarticons@1.2.2':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/prime@1.2.2':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify-json/streamline-freehand@1.2.1':
+    dependencies:
+      '@iconify/types': 2.0.0
+
+  '@iconify/types@2.0.0': {}
+
+  '@iconify/utils@2.3.0':
+    dependencies:
+      '@antfu/install-pkg': 1.1.0
+      '@antfu/utils': 8.1.1
+      '@iconify/types': 2.0.0
+      debug: 4.4.1
+      globals: 15.15.0
+      kolorist: 1.8.0
+      local-pkg: 1.1.1
+      mlly: 1.7.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@isaacs/balanced-match@4.0.1': {}
+
+  '@isaacs/brace-expansion@5.0.0':
+    dependencies:
+      '@isaacs/balanced-match': 4.0.1
+
+  '@isaacs/cliui@8.0.2':
+    dependencies:
+      string-width: 5.1.2
+      string-width-cjs: string-width@4.2.3
+      strip-ansi: 7.1.0
+      strip-ansi-cjs: strip-ansi@6.0.1
+      wrap-ansi: 8.1.0
+      wrap-ansi-cjs: wrap-ansi@7.0.0
+
+  '@jridgewell/gen-mapping@0.3.12':
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.4
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/source-map@0.3.10':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.12
+      '@jridgewell/trace-mapping': 0.3.29
+
+  '@jridgewell/sourcemap-codec@1.5.4': {}
+
+  '@jridgewell/trace-mapping@0.3.29':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.4
+
+  '@juggle/resize-observer@3.4.0': {}
+
+  '@napi-rs/wasm-runtime@1.0.1':
+    dependencies:
+      '@emnapi/core': 1.4.5
+      '@emnapi/runtime': 1.4.5
+      '@tybys/wasm-util': 0.10.0
+    optional: true
+
+  '@noble/ciphers@1.3.0': {}
+
+  '@noble/curves@1.9.6':
+    dependencies:
+      '@noble/hashes': 1.8.0
+
+  '@noble/hashes@1.8.0': {}
+
+  '@nodelib/fs.scandir@2.1.5':
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+
+  '@nodelib/fs.stat@2.0.5': {}
+
+  '@nodelib/fs.walk@1.2.8':
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.19.1
+
+  '@one-ini/wasm@0.1.1': {}
+
+  '@oxc-project/runtime@0.80.0': {}
+
+  '@oxc-project/types@0.80.0': {}
+
+  '@oxc-resolver/binding-android-arm-eabi@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-android-arm64@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-darwin-arm64@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-darwin-x64@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-freebsd-x64@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-arm-gnueabihf@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-arm-musleabihf@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-arm64-gnu@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-arm64-musl@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-ppc64-gnu@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-riscv64-gnu@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-riscv64-musl@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-s390x-gnu@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-x64-gnu@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-linux-x64-musl@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-wasm32-wasi@11.6.1':
+    dependencies:
+      '@napi-rs/wasm-runtime': 1.0.1
+    optional: true
+
+  '@oxc-resolver/binding-win32-arm64-msvc@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-win32-ia32-msvc@11.6.1':
+    optional: true
+
+  '@oxc-resolver/binding-win32-x64-msvc@11.6.1':
+    optional: true
+
+  '@parcel/watcher-android-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-darwin-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-darwin-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-freebsd-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-x64-glibc@2.5.1':
+    optional: true
+
+  '@parcel/watcher-linux-x64-musl@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-arm64@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-ia32@2.5.1':
+    optional: true
+
+  '@parcel/watcher-win32-x64@2.5.1':
+    optional: true
+
+  '@parcel/watcher@2.5.1':
+    dependencies:
+      detect-libc: 1.0.3
+      is-glob: 4.0.3
+      micromatch: 4.0.8
+      node-addon-api: 7.1.1
+    optionalDependencies:
+      '@parcel/watcher-android-arm64': 2.5.1
+      '@parcel/watcher-darwin-arm64': 2.5.1
+      '@parcel/watcher-darwin-x64': 2.5.1
+      '@parcel/watcher-freebsd-x64': 2.5.1
+      '@parcel/watcher-linux-arm-glibc': 2.5.1
+      '@parcel/watcher-linux-arm-musl': 2.5.1
+      '@parcel/watcher-linux-arm64-glibc': 2.5.1
+      '@parcel/watcher-linux-arm64-musl': 2.5.1
+      '@parcel/watcher-linux-x64-glibc': 2.5.1
+      '@parcel/watcher-linux-x64-musl': 2.5.1
+      '@parcel/watcher-win32-arm64': 2.5.1
+      '@parcel/watcher-win32-ia32': 2.5.1
+      '@parcel/watcher-win32-x64': 2.5.1
+    optional: true
+
+  '@pkgjs/parseargs@0.11.0':
+    optional: true
+
+  '@pkgr/core@0.2.9': {}
+
+  '@playwright/test@1.54.2':
+    dependencies:
+      playwright: 1.54.2
+
+  '@polka/url@1.0.0-next.29': {}
+
+  '@publint/pack@0.1.2': {}
+
+  '@quansync/fs@0.1.3':
+    dependencies:
+      quansync: 0.2.10
+
+  '@rolldown/binding-android-arm64@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-darwin-arm64@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-darwin-x64@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-freebsd-x64@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-linux-arm64-musl@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-linux-arm64-ohos@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-linux-x64-gnu@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-linux-x64-musl@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-wasm32-wasi@1.0.0-beta.31':
+    dependencies:
+      '@napi-rs/wasm-runtime': 1.0.1
+    optional: true
+
+  '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/binding-win32-x64-msvc@1.0.0-beta.31':
+    optional: true
+
+  '@rolldown/pluginutils@1.0.0-beta.29': {}
+
+  '@rolldown/pluginutils@1.0.0-beta.31': {}
+
+  '@rollup/pluginutils@4.2.1':
+    dependencies:
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+
+  '@rollup/rollup-android-arm-eabi@4.46.2':
+    optional: true
+
+  '@rollup/rollup-android-arm64@4.46.2':
+    optional: true
+
+  '@rollup/rollup-darwin-arm64@4.46.2':
+    optional: true
+
+  '@rollup/rollup-darwin-x64@4.46.2':
+    optional: true
+
+  '@rollup/rollup-freebsd-arm64@4.46.2':
+    optional: true
+
+  '@rollup/rollup-freebsd-x64@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm-musleabihf@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-gnu@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-musl@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-ppc64-gnu@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-gnu@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-musl@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-s390x-gnu@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-x64-gnu@4.46.2':
+    optional: true
+
+  '@rollup/rollup-linux-x64-musl@4.46.2':
+    optional: true
+
+  '@rollup/rollup-win32-arm64-msvc@4.46.2':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.46.2':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.46.2':
+    optional: true
+
+  '@sec-ant/readable-stream@0.4.1': {}
+
+  '@sindresorhus/merge-streams@4.0.0': {}
+
+  '@stylistic/eslint-plugin@5.2.2(eslint@9.32.0(jiti@2.5.1))':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      '@typescript-eslint/types': 8.39.0
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      estraverse: 5.3.0
+      picomatch: 4.0.3
+
+  '@ts-morph/common@0.27.0':
+    dependencies:
+      fast-glob: 3.3.3
+      minimatch: 10.0.3
+      path-browserify: 1.0.1
+
+  '@tsconfig/node22@22.0.2': {}
+
+  '@tybys/wasm-util@0.10.0':
+    dependencies:
+      tslib: 2.8.1
+    optional: true
+
+  '@types/chai@5.2.2':
+    dependencies:
+      '@types/deep-eql': 4.0.2
+
+  '@types/chroma-js@3.1.1': {}
+
+  '@types/d3-array@3.2.1': {}
+
+  '@types/d3-color@3.1.3': {}
+
+  '@types/d3-dispatch@3.0.7': {}
+
+  '@types/d3-dsv@3.0.7': {}
+
+  '@types/d3-ease@3.0.2': {}
+
+  '@types/d3-fetch@3.0.7':
+    dependencies:
+      '@types/d3-dsv': 3.0.7
+
+  '@types/d3-force@3.0.10': {}
+
+  '@types/d3-format@3.0.4': {}
+
+  '@types/d3-geo@3.1.0':
+    dependencies:
+      '@types/geojson': 7946.0.16
+
+  '@types/d3-hierarchy@3.1.7': {}
+
+  '@types/d3-interpolate@3.0.4':
+    dependencies:
+      '@types/d3-color': 3.1.3
+
+  '@types/d3-path@3.1.1': {}
+
+  '@types/d3-quadtree@3.0.6': {}
+
+  '@types/d3-random@3.0.3': {}
+
+  '@types/d3-scale-chromatic@3.1.0': {}
+
+  '@types/d3-scale@4.0.9':
+    dependencies:
+      '@types/d3-time': 3.0.4
+
+  '@types/d3-shape@3.1.7':
+    dependencies:
+      '@types/d3-path': 3.1.1
+
+  '@types/d3-time@3.0.4': {}
+
+  '@types/d3-timer@3.0.2': {}
+
+  '@types/debug@4.1.12':
+    dependencies:
+      '@types/ms': 2.1.0
+
+  '@types/deep-eql@4.0.2': {}
+
+  '@types/estree@1.0.8': {}
+
+  '@types/geojson@7946.0.16': {}
+
+  '@types/jsdom@21.1.7':
+    dependencies:
+      '@types/node': 22.17.0
+      '@types/tough-cookie': 4.0.5
+      parse5: 7.3.0
+
+  '@types/json-schema@7.0.15': {}
+
+  '@types/katex@0.16.7': {}
+
+  '@types/lodash-es@4.17.12':
+    dependencies:
+      '@types/lodash': 4.17.20
+
+  '@types/lodash@4.17.20': {}
+
+  '@types/mdast@4.0.4':
+    dependencies:
+      '@types/unist': 3.0.3
+
+  '@types/ms@2.1.0': {}
+
+  '@types/node@22.17.0':
+    dependencies:
+      undici-types: 6.21.0
+
+  '@types/sortablejs@1.15.8': {}
+
+  '@types/tough-cookie@4.0.5': {}
+
+  '@types/unist@3.0.3': {}
+
+  '@types/web-bluetooth@0.0.21': {}
+
+  '@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
+    dependencies:
+      '@eslint-community/regexpp': 4.12.1
+      '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      '@typescript-eslint/scope-manager': 8.39.0
+      '@typescript-eslint/type-utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      '@typescript-eslint/visitor-keys': 8.39.0
+      eslint: 9.32.0(jiti@2.5.1)
+      graphemer: 1.4.0
+      ignore: 7.0.5
+      natural-compare: 1.4.0
+      ts-api-utils: 2.1.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/scope-manager': 8.39.0
+      '@typescript-eslint/types': 8.39.0
+      '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.8.3)
+      '@typescript-eslint/visitor-keys': 8.39.0
+      debug: 4.4.1
+      eslint: 9.32.0(jiti@2.5.1)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/project-service@8.39.0(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.8.3)
+      '@typescript-eslint/types': 8.39.0
+      debug: 4.4.1
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/scope-manager@8.39.0':
+    dependencies:
+      '@typescript-eslint/types': 8.39.0
+      '@typescript-eslint/visitor-keys': 8.39.0
+
+  '@typescript-eslint/tsconfig-utils@8.39.0(typescript@5.8.3)':
+    dependencies:
+      typescript: 5.8.3
+
+  '@typescript-eslint/type-utils@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/types': 8.39.0
+      '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      debug: 4.4.1
+      eslint: 9.32.0(jiti@2.5.1)
+      ts-api-utils: 2.1.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/types@8.39.0': {}
+
+  '@typescript-eslint/typescript-estree@8.39.0(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/project-service': 8.39.0(typescript@5.8.3)
+      '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.8.3)
+      '@typescript-eslint/types': 8.39.0
+      '@typescript-eslint/visitor-keys': 8.39.0
+      debug: 4.4.1
+      fast-glob: 3.3.3
+      is-glob: 4.0.3
+      minimatch: 9.0.5
+      semver: 7.7.2
+      ts-api-utils: 2.1.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/utils@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      '@typescript-eslint/scope-manager': 8.39.0
+      '@typescript-eslint/types': 8.39.0
+      '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.8.3)
+      eslint: 9.32.0(jiti@2.5.1)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/visitor-keys@8.39.0':
+    dependencies:
+      '@typescript-eslint/types': 8.39.0
+      eslint-visitor-keys: 4.2.1
+
+  '@typescript/native-preview-darwin-arm64@7.0.0-dev.20250819.1':
+    optional: true
+
+  '@typescript/native-preview-darwin-x64@7.0.0-dev.20250819.1':
+    optional: true
+
+  '@typescript/native-preview-linux-arm64@7.0.0-dev.20250819.1':
+    optional: true
+
+  '@typescript/native-preview-linux-arm@7.0.0-dev.20250819.1':
+    optional: true
+
+  '@typescript/native-preview-linux-x64@7.0.0-dev.20250819.1':
+    optional: true
+
+  '@typescript/native-preview-win32-arm64@7.0.0-dev.20250819.1':
+    optional: true
+
+  '@typescript/native-preview-win32-x64@7.0.0-dev.20250819.1':
+    optional: true
+
+  '@typescript/native-preview@7.0.0-dev.20250819.1':
+    optionalDependencies:
+      '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20250819.1
+      '@typescript/native-preview-darwin-x64': 7.0.0-dev.20250819.1
+      '@typescript/native-preview-linux-arm': 7.0.0-dev.20250819.1
+      '@typescript/native-preview-linux-arm64': 7.0.0-dev.20250819.1
+      '@typescript/native-preview-linux-x64': 7.0.0-dev.20250819.1
+      '@typescript/native-preview-win32-arm64': 7.0.0-dev.20250819.1
+      '@typescript/native-preview-win32-x64': 7.0.0-dev.20250819.1
+
+  '@umami/node@0.4.0': {}
+
+  '@unocss/astro@66.4.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/reset': 66.4.1
+      '@unocss/vite': 66.4.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+    optionalDependencies:
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+
+  '@unocss/cli@66.4.1':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@unocss/config': 66.4.1
+      '@unocss/core': 66.4.1
+      '@unocss/preset-uno': 66.4.1
+      cac: 6.7.14
+      chokidar: 3.6.0
+      colorette: 2.0.20
+      consola: 3.4.2
+      magic-string: 0.30.17
+      pathe: 2.0.3
+      perfect-debounce: 1.0.0
+      tinyglobby: 0.2.14
+      unplugin-utils: 0.2.5
+
+  '@unocss/config@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      unconfig: 7.3.2
+
+  '@unocss/core@66.4.1': {}
+
+  '@unocss/extractor-arbitrary-variants@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+
+  '@unocss/inspector@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/rule-utils': 66.4.1
+      colorette: 2.0.20
+      gzip-size: 6.0.0
+      sirv: 3.0.1
+      vue-flow-layout: 0.2.0
+
+  '@unocss/postcss@66.4.1(postcss@8.5.6)':
+    dependencies:
+      '@unocss/config': 66.4.1
+      '@unocss/core': 66.4.1
+      '@unocss/rule-utils': 66.4.1
+      css-tree: 3.1.0
+      postcss: 8.5.6
+      tinyglobby: 0.2.14
+
+  '@unocss/preset-attributify@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+
+  '@unocss/preset-icons@66.4.1':
+    dependencies:
+      '@iconify/utils': 2.3.0
+      '@unocss/core': 66.4.1
+      ofetch: 1.4.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@unocss/preset-mini@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/extractor-arbitrary-variants': 66.4.1
+      '@unocss/rule-utils': 66.4.1
+
+  '@unocss/preset-tagify@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+
+  '@unocss/preset-typography@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/preset-mini': 66.4.1
+      '@unocss/rule-utils': 66.4.1
+
+  '@unocss/preset-uno@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/preset-wind3': 66.4.1
+
+  '@unocss/preset-web-fonts@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      ofetch: 1.4.1
+
+  '@unocss/preset-wind3@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/preset-mini': 66.4.1
+      '@unocss/rule-utils': 66.4.1
+
+  '@unocss/preset-wind4@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/extractor-arbitrary-variants': 66.4.1
+      '@unocss/rule-utils': 66.4.1
+
+  '@unocss/preset-wind@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/preset-wind3': 66.4.1
+
+  '@unocss/reset@66.4.1': {}
+
+  '@unocss/rule-utils@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      magic-string: 0.30.17
+
+  '@unocss/transformer-attributify-jsx@66.4.1':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@babel/traverse': 7.28.0
+      '@unocss/core': 66.4.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@unocss/transformer-compile-class@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+
+  '@unocss/transformer-directives@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+      '@unocss/rule-utils': 66.4.1
+      css-tree: 3.1.0
+
+  '@unocss/transformer-variant-group@66.4.1':
+    dependencies:
+      '@unocss/core': 66.4.1
+
+  '@unocss/vite@66.4.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@unocss/config': 66.4.1
+      '@unocss/core': 66.4.1
+      '@unocss/inspector': 66.4.1
+      chokidar: 3.6.0
+      magic-string: 0.30.17
+      pathe: 2.0.3
+      tinyglobby: 0.2.14
+      unplugin-utils: 0.2.5
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+
+  '@vitejs/plugin-vue-jsx@5.0.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0)
+      '@rolldown/pluginutils': 1.0.0-beta.31
+      '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0)
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vue: 3.5.18(typescript@5.8.3)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vitejs/plugin-vue@6.0.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@rolldown/pluginutils': 1.0.0-beta.29
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vitest/eslint-plugin@1.3.4(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)(vitest@3.2.4)':
+    dependencies:
+      '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      eslint: 9.32.0(jiti@2.5.1)
+    optionalDependencies:
+      typescript: 5.8.3
+      vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.0)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vitest/expect@3.2.4':
+    dependencies:
+      '@types/chai': 5.2.2
+      '@vitest/spy': 3.2.4
+      '@vitest/utils': 3.2.4
+      chai: 5.2.1
+      tinyrainbow: 2.0.0
+
+  '@vitest/mocker@3.2.4(vite@7.1.0(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))':
+    dependencies:
+      '@vitest/spy': 3.2.4
+      estree-walker: 3.0.3
+      magic-string: 0.30.17
+    optionalDependencies:
+      vite: 7.1.0(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+
+  '@vitest/pretty-format@3.2.4':
+    dependencies:
+      tinyrainbow: 2.0.0
+
+  '@vitest/runner@3.2.4':
+    dependencies:
+      '@vitest/utils': 3.2.4
+      pathe: 2.0.3
+      strip-literal: 3.0.0
+
+  '@vitest/snapshot@3.2.4':
+    dependencies:
+      '@vitest/pretty-format': 3.2.4
+      magic-string: 0.30.17
+      pathe: 2.0.3
+
+  '@vitest/spy@3.2.4':
+    dependencies:
+      tinyspy: 4.0.3
+
+  '@vitest/ui@3.2.4(vitest@3.2.4)':
+    dependencies:
+      '@vitest/utils': 3.2.4
+      fflate: 0.8.2
+      flatted: 3.3.3
+      pathe: 2.0.3
+      sirv: 3.0.1
+      tinyglobby: 0.2.14
+      tinyrainbow: 2.0.0
+      vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.0)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+
+  '@vitest/utils@3.2.4':
+    dependencies:
+      '@vitest/pretty-format': 3.2.4
+      loupe: 3.2.0
+      tinyrainbow: 2.0.0
+
+  '@volar/language-core@2.4.22':
+    dependencies:
+      '@volar/source-map': 2.4.22
+
+  '@volar/language-server@2.4.22':
+    dependencies:
+      '@volar/language-core': 2.4.22
+      '@volar/language-service': 2.4.22
+      '@volar/typescript': 2.4.22
+      path-browserify: 1.0.1
+      request-light: 0.7.0
+      vscode-languageserver: 9.0.1
+      vscode-languageserver-protocol: 3.17.5
+      vscode-languageserver-textdocument: 1.0.12
+      vscode-uri: 3.1.0
+
+  '@volar/language-service@2.4.22':
+    dependencies:
+      '@volar/language-core': 2.4.22
+      vscode-languageserver-protocol: 3.17.5
+      vscode-languageserver-textdocument: 1.0.12
+      vscode-uri: 3.1.0
+
+  '@volar/source-map@2.4.22': {}
+
+  '@volar/typescript@2.4.22':
+    dependencies:
+      '@volar/language-core': 2.4.22
+      path-browserify: 1.0.1
+      vscode-uri: 3.1.0
+
+  '@vue-macros/api@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      neverthrow: 8.2.0
+      oxc-resolver: 11.6.1
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/better-define@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/api': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      neverthrow: 8.2.0
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/boolean-prop@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-core': 3.5.18
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/chain-call@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/common@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue/compiler-sfc': 3.5.18
+      ast-kit: 2.1.1
+      local-pkg: 1.1.1
+      magic-string-ast: 1.0.0
+      unplugin-utils: 0.2.5
+    optionalDependencies:
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/config@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      quansync: 0.2.10
+      unconfig: 7.3.2
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/define-emit@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/define-models@3.0.0-beta.21(@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3)))(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      ast-walker-scope: 0.8.1
+      unplugin: 2.3.5
+    optionalDependencies:
+      '@vueuse/core': 13.6.0(vue@3.5.18(typescript@5.8.3))
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/define-prop@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/api': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/define-props-refs@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/define-props@3.0.0-beta.21(@vue-macros/reactivity-transform@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3)))(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/reactivity-transform': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/define-render@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/define-slots@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/define-stylex@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-dom': 3.5.18
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/devtools@3.0.0-beta.21(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(typescript@5.8.3)':
+    dependencies:
+      sirv: 3.0.1
+      vue: 3.5.18(typescript@5.8.3)
+    optionalDependencies:
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+    transitivePeerDependencies:
+      - typescript
+
+  '@vue-macros/export-expose@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-sfc': 3.5.18
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/export-props@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/export-render@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/hoist-static@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/jsx-directive@3.0.0-beta.21(typescript@5.8.3)':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+    transitivePeerDependencies:
+      - typescript
+
+  '@vue-macros/named-template@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-dom': 3.5.18
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/reactivity-transform@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-core': 3.5.18
+      '@vue/shared': 3.5.18
+      magic-string: 0.30.17
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/script-lang@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue-macros/setup-block@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-dom': 3.5.18
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/setup-component@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/setup-sfc@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/short-bind@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-core': 3.5.18
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/short-emits@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/short-vmodel@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/compiler-core': 3.5.18
+    transitivePeerDependencies:
+      - vue
+
+  '@vue-macros/volar@3.0.0-beta.21(typescript@5.8.3)(vue-tsc@3.0.5(typescript@5.8.3))(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue-macros/boolean-prop': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/config': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/short-bind': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/short-vmodel': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue/language-core': 3.0.5(typescript@5.8.3)
+      '@vue/shared': 3.5.18
+      muggle-string: 0.4.1
+      ts-macro: 0.3.2
+    optionalDependencies:
+      vue-tsc: 3.0.5(typescript@5.8.3)
+    transitivePeerDependencies:
+      - typescript
+      - vue
+
+  '@vue-vine/compiler@1.7.0':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@babel/types': 7.28.2
+      '@vue/compiler-dom': 3.5.18
+      '@vue/compiler-ssr': 3.5.18
+      '@vue/shared': 3.5.18
+      get-tsconfig: 4.10.1
+      hash-sum: 2.0.0
+      line-column: 1.0.2
+      lru-cache: 11.1.0
+      magic-string: 0.30.17
+      merge-source-map: 1.1.0
+      postcss: 8.5.6
+      postcss-selector-parser: 7.1.0
+      ts-morph: 26.0.0
+
+  '@vue-vine/language-service@1.7.0(typescript@5.8.3)':
+    dependencies:
+      '@umami/node': 0.4.0
+      '@volar/language-core': 2.4.22
+      '@volar/language-server': 2.4.22
+      '@volar/typescript': 2.4.22
+      '@vue-vine/compiler': 1.7.0
+      '@vue/language-core': 3.0.5(typescript@5.8.3)
+      '@vue/shared': 3.5.18
+      destr: 2.0.5
+      detect-port: 2.1.0
+      muggle-string: 0.4.1
+      pathe: 2.0.3
+      ws: 8.18.3
+    transitivePeerDependencies:
+      - bufferutil
+      - typescript
+      - utf-8-validate
+
+  '@vue/babel-helper-vue-transform-on@1.4.0': {}
+
+  '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0)
+      '@babel/template': 7.27.2
+      '@babel/traverse': 7.28.0
+      '@babel/types': 7.28.2
+      '@vue/babel-helper-vue-transform-on': 1.4.0
+      '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.28.0)
+      '@vue/shared': 3.5.18
+    optionalDependencies:
+      '@babel/core': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.28.0)':
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      '@babel/core': 7.28.0
+      '@babel/helper-module-imports': 7.27.1
+      '@babel/helper-plugin-utils': 7.27.1
+      '@babel/parser': 7.28.0
+      '@vue/compiler-sfc': 3.5.18
+    transitivePeerDependencies:
+      - supports-color
+
+  '@vue/compiler-core@3.5.18':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/shared': 3.5.18
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.1
+
+  '@vue/compiler-dom@3.5.18':
+    dependencies:
+      '@vue/compiler-core': 3.5.18
+      '@vue/shared': 3.5.18
+
+  '@vue/compiler-sfc@3.5.18':
+    dependencies:
+      '@babel/parser': 7.28.0
+      '@vue/compiler-core': 3.5.18
+      '@vue/compiler-dom': 3.5.18
+      '@vue/compiler-ssr': 3.5.18
+      '@vue/shared': 3.5.18
+      estree-walker: 2.0.2
+      magic-string: 0.30.17
+      postcss: 8.5.6
+      source-map-js: 1.2.1
+
+  '@vue/compiler-ssr@3.5.18':
+    dependencies:
+      '@vue/compiler-dom': 3.5.18
+      '@vue/shared': 3.5.18
+
+  '@vue/compiler-vue2@2.7.16':
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
+
+  '@vue/devtools-api@6.6.4': {}
+
+  '@vue/devtools-api@7.7.7':
+    dependencies:
+      '@vue/devtools-kit': 7.7.7
+
+  '@vue/devtools-core@7.7.7(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue/devtools-kit': 7.7.7
+      '@vue/devtools-shared': 7.7.7
+      mitt: 3.0.1
+      nanoid: 5.1.5
+      pathe: 2.0.3
+      vite-hot-client: 2.1.0(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+      vue: 3.5.18(typescript@5.8.3)
+    transitivePeerDependencies:
+      - vite
+
+  '@vue/devtools-kit@7.7.7':
+    dependencies:
+      '@vue/devtools-shared': 7.7.7
+      birpc: 2.5.0
+      hookable: 5.5.3
+      mitt: 3.0.1
+      perfect-debounce: 1.0.0
+      speakingurl: 14.0.1
+      superjson: 2.2.2
+
+  '@vue/devtools-shared@7.7.7':
+    dependencies:
+      rfdc: 1.4.1
+
+  '@vue/eslint-config-prettier@10.2.0(eslint@9.32.0(jiti@2.5.1))(prettier@3.5.3)':
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-config-prettier: 10.1.8(eslint@9.32.0(jiti@2.5.1))
+      eslint-plugin-prettier: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1))(prettier@3.5.3)
+      prettier: 3.5.3
+    transitivePeerDependencies:
+      - '@types/eslint'
+
+  '@vue/language-core@3.0.5(typescript@5.8.3)':
+    dependencies:
+      '@volar/language-core': 2.4.22
+      '@vue/compiler-dom': 3.5.18
+      '@vue/compiler-vue2': 2.7.16
+      '@vue/shared': 3.5.18
+      alien-signals: 2.0.6
+      muggle-string: 0.4.1
+      path-browserify: 1.0.1
+      picomatch: 4.0.3
+    optionalDependencies:
+      typescript: 5.8.3
+
+  '@vue/reactivity@3.5.18':
+    dependencies:
+      '@vue/shared': 3.5.18
+
+  '@vue/runtime-core@3.5.18':
+    dependencies:
+      '@vue/reactivity': 3.5.18
+      '@vue/shared': 3.5.18
+
+  '@vue/runtime-dom@3.5.18':
+    dependencies:
+      '@vue/reactivity': 3.5.18
+      '@vue/runtime-core': 3.5.18
+      '@vue/shared': 3.5.18
+      csstype: 3.1.3
+
+  '@vue/server-renderer@3.5.18(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.18
+      '@vue/shared': 3.5.18
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vue/shared@3.5.18': {}
+
+  '@vue/test-utils@2.4.6':
+    dependencies:
+      js-beautify: 1.15.4
+      vue-component-type-helpers: 2.2.12
+
+  '@vue/tsconfig@0.7.0(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))':
+    optionalDependencies:
+      typescript: 5.8.3
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vueuse/components@13.6.0(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@vueuse/core': 13.6.0(vue@3.5.18(typescript@5.8.3))
+      '@vueuse/shared': 13.6.0(vue@3.5.18(typescript@5.8.3))
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      '@types/web-bluetooth': 0.0.21
+      '@vueuse/metadata': 13.6.0
+      '@vueuse/shared': 13.6.0(vue@3.5.18(typescript@5.8.3))
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@vueuse/metadata@13.6.0': {}
+
+  '@vueuse/shared@13.6.0(vue@3.5.18(typescript@5.8.3))':
+    dependencies:
+      vue: 3.5.18(typescript@5.8.3)
+
+  '@webgpu/types@0.1.64': {}
+
+  abbrev@2.0.0: {}
+
+  acorn-jsx@5.3.2(acorn@8.15.0):
+    dependencies:
+      acorn: 8.15.0
+
+  acorn@8.15.0: {}
+
+  address@2.0.3: {}
+
+  agent-base@7.1.4: {}
+
+  ajv@6.12.6:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+
+  alien-signals@2.0.6: {}
+
+  ansi-regex@5.0.1: {}
+
+  ansi-regex@6.1.0: {}
+
+  ansi-styles@4.3.0:
+    dependencies:
+      color-convert: 2.0.1
+
+  ansi-styles@6.2.1: {}
+
+  ansis@4.1.0: {}
+
+  anymatch@3.1.3:
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+
+  are-docs-informative@0.0.2: {}
+
+  argparse@2.0.1: {}
+
+  assertion-error@2.0.1: {}
+
+  ast-kit@2.1.1:
+    dependencies:
+      '@babel/parser': 7.28.0
+      pathe: 2.0.3
+
+  ast-walker-scope@0.8.1:
+    dependencies:
+      '@babel/parser': 7.28.0
+      ast-kit: 2.1.1
+
+  async-validator@4.2.5: {}
+
+  async@3.2.6: {}
+
+  balanced-match@1.0.2: {}
+
+  binary-extensions@2.3.0: {}
+
+  birpc@2.5.0: {}
+
+  boolbase@1.0.0: {}
+
+  brace-expansion@1.1.12:
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+
+  brace-expansion@2.0.2:
+    dependencies:
+      balanced-match: 1.0.2
+
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+
+  browserslist@4.25.1:
+    dependencies:
+      caniuse-lite: 1.0.30001731
+      electron-to-chromium: 1.5.198
+      node-releases: 2.0.19
+      update-browserslist-db: 1.1.3(browserslist@4.25.1)
+
+  buffer-builder@0.2.0: {}
+
+  buffer-from@1.1.2: {}
+
+  builtin-modules@5.0.0: {}
+
+  bundle-name@4.1.0:
+    dependencies:
+      run-applescript: 7.0.0
+
+  cac@6.7.14: {}
+
+  callsites@3.1.0: {}
+
+  camel-case@4.1.2:
+    dependencies:
+      pascal-case: 3.1.2
+      tslib: 2.8.1
+
+  caniuse-lite@1.0.30001731: {}
+
+  ccount@2.0.1: {}
+
+  chai@5.2.1:
+    dependencies:
+      assertion-error: 2.0.1
+      check-error: 2.1.1
+      deep-eql: 5.0.2
+      loupe: 3.2.0
+      pathval: 2.0.1
+
+  chalk@4.1.2:
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+
+  change-case@5.4.4: {}
+
+  character-entities@2.0.2: {}
+
+  check-error@2.1.1: {}
+
+  chokidar@3.6.0:
+    dependencies:
+      anymatch: 3.1.3
+      braces: 3.0.3
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  chokidar@4.0.3:
+    dependencies:
+      readdirp: 4.1.2
+
+  chroma-js@3.1.2: {}
+
+  ci-info@4.3.0: {}
+
+  clean-css@5.3.3:
+    dependencies:
+      source-map: 0.6.1
+
+  clean-regexp@1.0.0:
+    dependencies:
+      escape-string-regexp: 1.0.5
+
+  code-block-writer@13.0.3: {}
+
+  color-convert@2.0.1:
+    dependencies:
+      color-name: 1.1.4
+
+  color-name@1.1.4: {}
+
+  color-string@1.9.1:
+    dependencies:
+      color-name: 1.1.4
+      simple-swizzle: 0.2.2
+
+  colorette@2.0.20: {}
+
+  colorjs.io@0.5.2: {}
+
+  commander@10.0.1: {}
+
+  commander@11.1.0: {}
+
+  commander@2.20.3: {}
+
+  commander@7.2.0: {}
+
+  commander@8.3.0: {}
+
+  comment-parser@1.4.1: {}
+
+  concat-map@0.0.1: {}
+
+  confbox@0.1.8: {}
+
+  confbox@0.2.2: {}
+
+  config-chain@1.1.13:
+    dependencies:
+      ini: 1.3.8
+      proto-list: 1.2.4
+
+  connect-history-api-fallback@1.6.0: {}
+
+  consola@2.15.3: {}
+
+  consola@3.4.2: {}
+
+  convert-source-map@2.0.0: {}
+
+  cookie-es@1.2.2: {}
+
+  copy-anything@3.0.5:
+    dependencies:
+      is-what: 4.1.16
+
+  core-js-compat@3.45.0:
+    dependencies:
+      browserslist: 4.25.1
+
+  cross-spawn@7.0.6:
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+
+  crossws@0.3.5:
+    dependencies:
+      uncrypto: 0.1.3
+
+  css-render@0.15.14:
+    dependencies:
+      '@emotion/hash': 0.8.0
+      csstype: 3.0.11
+
+  css-select@4.3.0:
+    dependencies:
+      boolbase: 1.0.0
+      css-what: 6.2.2
+      domhandler: 4.3.1
+      domutils: 2.8.0
+      nth-check: 2.1.1
+
+  css-tree@3.1.0:
+    dependencies:
+      mdn-data: 2.12.2
+      source-map-js: 1.2.1
+
+  css-what@6.2.2: {}
+
+  cssesc@3.0.0: {}
+
+  cssstyle@4.6.0:
+    dependencies:
+      '@asamuzakjp/css-color': 3.2.0
+      rrweb-cssom: 0.8.0
+
+  csstype@3.0.11: {}
+
+  csstype@3.1.3: {}
+
+  d3-array@3.2.4:
+    dependencies:
+      internmap: 2.0.3
+
+  d3-binarytree@1.0.2: {}
+
+  d3-color@3.1.0: {}
+
+  d3-dispatch@3.0.1: {}
+
+  d3-dsv@3.0.1:
+    dependencies:
+      commander: 7.2.0
+      iconv-lite: 0.6.3
+      rw: 1.3.3
+
+  d3-ease@3.0.1: {}
+
+  d3-fetch@3.0.1:
+    dependencies:
+      d3-dsv: 3.0.1
+
+  d3-force-3d@3.0.6:
+    dependencies:
+      d3-binarytree: 1.0.2
+      d3-dispatch: 3.0.1
+      d3-octree: 1.1.0
+      d3-quadtree: 3.0.1
+      d3-timer: 3.0.1
+
+  d3-force@3.0.0:
+    dependencies:
+      d3-dispatch: 3.0.1
+      d3-quadtree: 3.0.1
+      d3-timer: 3.0.1
+
+  d3-format@3.1.0: {}
+
+  d3-geo-projection@4.0.0:
+    dependencies:
+      commander: 7.2.0
+      d3-array: 3.2.4
+      d3-geo: 3.1.1
+
+  d3-geo@3.1.1:
+    dependencies:
+      d3-array: 3.2.4
+
+  d3-hierarchy@3.1.2: {}
+
+  d3-interpolate@3.0.1:
+    dependencies:
+      d3-color: 3.1.0
+
+  d3-octree@1.1.0: {}
+
+  d3-path@3.1.0: {}
+
+  d3-quadtree@3.0.1: {}
+
+  d3-random@3.0.1: {}
+
+  d3-regression@1.3.10: {}
+
+  d3-scale-chromatic@3.1.0:
+    dependencies:
+      d3-color: 3.1.0
+      d3-interpolate: 3.0.1
+
+  d3-scale@4.0.2:
+    dependencies:
+      d3-array: 3.2.4
+      d3-format: 3.1.0
+      d3-interpolate: 3.0.1
+      d3-time: 3.1.0
+      d3-time-format: 4.1.0
+
+  d3-shape@3.2.0:
+    dependencies:
+      d3-path: 3.1.0
+
+  d3-time-format@4.1.0:
+    dependencies:
+      d3-time: 3.1.0
+
+  d3-time@3.1.0:
+    dependencies:
+      d3-array: 3.2.4
+
+  d3-timer@3.0.1: {}
+
+  data-urls@5.0.0:
+    dependencies:
+      whatwg-mimetype: 4.0.0
+      whatwg-url: 14.2.0
+
+  date-fns-tz@3.2.0(date-fns@3.6.0):
+    dependencies:
+      date-fns: 3.6.0
+
+  date-fns@3.6.0: {}
+
+  de-indent@1.0.2: {}
+
+  debug@4.4.1:
+    dependencies:
+      ms: 2.1.3
+
+  decimal.js@10.6.0: {}
+
+  decode-named-character-reference@1.2.0:
+    dependencies:
+      character-entities: 2.0.2
+
+  deep-eql@5.0.2: {}
+
+  deep-is@0.1.4: {}
+
+  deep-pick-omit@1.2.1: {}
+
+  default-browser-id@5.0.0: {}
+
+  default-browser@5.2.1:
+    dependencies:
+      bundle-name: 4.1.0
+      default-browser-id: 5.0.0
+
+  define-lazy-prop@3.0.0: {}
+
+  defu@6.1.4: {}
+
+  dequal@2.0.3: {}
+
+  destr@2.0.5: {}
+
+  detect-libc@1.0.3:
+    optional: true
+
+  detect-libc@2.0.4: {}
+
+  detect-port@2.1.0:
+    dependencies:
+      address: 2.0.3
+
+  devlop@1.1.0:
+    dependencies:
+      dequal: 2.0.3
+
+  dom-serializer@1.4.1:
+    dependencies:
+      domelementtype: 2.3.0
+      domhandler: 4.3.1
+      entities: 2.2.0
+
+  domelementtype@2.3.0: {}
+
+  domhandler@4.3.1:
+    dependencies:
+      domelementtype: 2.3.0
+
+  domutils@2.8.0:
+    dependencies:
+      dom-serializer: 1.4.1
+      domelementtype: 2.3.0
+      domhandler: 4.3.1
+
+  dot-case@3.0.4:
+    dependencies:
+      no-case: 3.0.4
+      tslib: 2.8.1
+
+  dotenv-expand@8.0.3: {}
+
+  dotenv@16.6.1: {}
+
+  dotenv@17.2.1: {}
+
+  duplexer@0.1.2: {}
+
+  earcut@2.2.4: {}
+
+  eastasianwidth@0.2.0: {}
+
+  eciesjs@0.4.15:
+    dependencies:
+      '@ecies/ciphers': 0.2.4(@noble/ciphers@1.3.0)
+      '@noble/ciphers': 1.3.0
+      '@noble/curves': 1.9.6
+      '@noble/hashes': 1.8.0
+
+  editorconfig@1.0.4:
+    dependencies:
+      '@one-ini/wasm': 0.1.1
+      commander: 10.0.1
+      minimatch: 9.0.1
+      semver: 7.7.2
+
+  ejs@3.1.10:
+    dependencies:
+      jake: 10.9.4
+
+  electron-to-chromium@1.5.198: {}
+
+  emoji-regex@8.0.0: {}
+
+  emoji-regex@9.2.2: {}
+
+  enhanced-resolve@5.18.3:
+    dependencies:
+      graceful-fs: 4.2.11
+      tapable: 2.2.2
+
+  entities@2.2.0: {}
+
+  entities@4.5.0: {}
+
+  entities@6.0.1: {}
+
+  error-stack-parser-es@1.0.5: {}
+
+  es-module-lexer@1.7.0: {}
+
+  es-toolkit@1.39.8: {}
+
+  esbuild@0.25.8:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.25.8
+      '@esbuild/android-arm': 0.25.8
+      '@esbuild/android-arm64': 0.25.8
+      '@esbuild/android-x64': 0.25.8
+      '@esbuild/darwin-arm64': 0.25.8
+      '@esbuild/darwin-x64': 0.25.8
+      '@esbuild/freebsd-arm64': 0.25.8
+      '@esbuild/freebsd-x64': 0.25.8
+      '@esbuild/linux-arm': 0.25.8
+      '@esbuild/linux-arm64': 0.25.8
+      '@esbuild/linux-ia32': 0.25.8
+      '@esbuild/linux-loong64': 0.25.8
+      '@esbuild/linux-mips64el': 0.25.8
+      '@esbuild/linux-ppc64': 0.25.8
+      '@esbuild/linux-riscv64': 0.25.8
+      '@esbuild/linux-s390x': 0.25.8
+      '@esbuild/linux-x64': 0.25.8
+      '@esbuild/netbsd-arm64': 0.25.8
+      '@esbuild/netbsd-x64': 0.25.8
+      '@esbuild/openbsd-arm64': 0.25.8
+      '@esbuild/openbsd-x64': 0.25.8
+      '@esbuild/openharmony-arm64': 0.25.8
+      '@esbuild/sunos-x64': 0.25.8
+      '@esbuild/win32-arm64': 0.25.8
+      '@esbuild/win32-ia32': 0.25.8
+      '@esbuild/win32-x64': 0.25.8
+
+  escalade@3.2.0: {}
+
+  escape-string-regexp@1.0.5: {}
+
+  escape-string-regexp@4.0.0: {}
+
+  escape-string-regexp@5.0.0: {}
+
+  eslint-compat-utils@0.5.1(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      semver: 7.7.2
+
+  eslint-compat-utils@0.6.5(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      semver: 7.7.2
+
+  eslint-config-flat-gitignore@2.1.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@eslint/compat': 1.3.1(eslint@9.32.0(jiti@2.5.1))
+      eslint: 9.32.0(jiti@2.5.1)
+
+  eslint-config-prettier@10.1.8(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+
+  eslint-flat-config-utils@2.1.1:
+    dependencies:
+      pathe: 2.0.3
+
+  eslint-json-compat-utils@0.2.1(eslint@9.32.0(jiti@2.5.1))(jsonc-eslint-parser@2.4.0):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      esquery: 1.6.0
+      jsonc-eslint-parser: 2.4.0
+
+  eslint-merge-processors@2.0.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+
+  eslint-plugin-antfu@3.1.1(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+
+  eslint-plugin-command@3.3.1(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@es-joy/jsdoccomment': 0.50.2
+      eslint: 9.32.0(jiti@2.5.1)
+
+  eslint-plugin-es-x@7.8.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      '@eslint-community/regexpp': 4.12.1
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-compat-utils: 0.5.1(eslint@9.32.0(jiti@2.5.1))
+
+  eslint-plugin-import-lite@0.3.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      '@typescript-eslint/types': 8.39.0
+      eslint: 9.32.0(jiti@2.5.1)
+    optionalDependencies:
+      typescript: 5.8.3
+
+  eslint-plugin-jsdoc@52.0.4(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@es-joy/jsdoccomment': 0.52.0
+      are-docs-informative: 0.0.2
+      comment-parser: 1.4.1
+      debug: 4.4.1
+      escape-string-regexp: 4.0.0
+      eslint: 9.32.0(jiti@2.5.1)
+      espree: 10.4.0
+      esquery: 1.6.0
+      parse-imports-exports: 0.2.4
+      semver: 7.7.2
+      spdx-expression-parse: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-plugin-jsonc@2.20.1(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-compat-utils: 0.6.5(eslint@9.32.0(jiti@2.5.1))
+      eslint-json-compat-utils: 0.2.1(eslint@9.32.0(jiti@2.5.1))(jsonc-eslint-parser@2.4.0)
+      espree: 10.4.0
+      graphemer: 1.4.0
+      jsonc-eslint-parser: 2.4.0
+      natural-compare: 1.4.0
+      synckit: 0.11.11
+    transitivePeerDependencies:
+      - '@eslint/json'
+
+  eslint-plugin-n@17.21.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      enhanced-resolve: 5.18.3
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-plugin-es-x: 7.8.0(eslint@9.32.0(jiti@2.5.1))
+      get-tsconfig: 4.10.1
+      globals: 15.15.0
+      globrex: 0.1.2
+      ignore: 5.3.2
+      semver: 7.7.2
+      ts-declaration-location: 1.0.7(typescript@5.8.3)
+    transitivePeerDependencies:
+      - typescript
+
+  eslint-plugin-no-only-tests@3.3.0: {}
+
+  eslint-plugin-perfectionist@4.15.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3):
+    dependencies:
+      '@typescript-eslint/types': 8.39.0
+      '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+      eslint: 9.32.0(jiti@2.5.1)
+      natural-orderby: 5.0.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+
+  eslint-plugin-playwright@2.2.2(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      globals: 13.24.0
+
+  eslint-plugin-pnpm@1.1.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      find-up-simple: 1.0.1
+      jsonc-eslint-parser: 2.4.0
+      pathe: 2.0.3
+      pnpm-workspace-yaml: 1.1.0
+      tinyglobby: 0.2.14
+      yaml-eslint-parser: 1.3.0
+
+  eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.32.0(jiti@2.5.1)))(eslint@9.32.0(jiti@2.5.1))(prettier@3.5.3):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+      prettier: 3.5.3
+      prettier-linter-helpers: 1.0.0
+      synckit: 0.11.11
+    optionalDependencies:
+      eslint-config-prettier: 10.1.8(eslint@9.32.0(jiti@2.5.1))
+
+  eslint-plugin-regexp@2.9.1(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      '@eslint-community/regexpp': 4.12.1
+      comment-parser: 1.4.1
+      eslint: 9.32.0(jiti@2.5.1)
+      jsdoc-type-pratt-parser: 4.1.0
+      refa: 0.12.1
+      regexp-ast-analysis: 0.7.1
+      scslre: 0.3.0
+
+  eslint-plugin-toml@0.12.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      debug: 4.4.1
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-compat-utils: 0.6.5(eslint@9.32.0(jiti@2.5.1))
+      lodash: 4.17.21
+      toml-eslint-parser: 0.10.0
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-plugin-unicorn@60.0.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@babel/helper-validator-identifier': 7.27.1
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      '@eslint/plugin-kit': 0.3.4
+      change-case: 5.4.4
+      ci-info: 4.3.0
+      clean-regexp: 1.0.0
+      core-js-compat: 3.45.0
+      eslint: 9.32.0(jiti@2.5.1)
+      esquery: 1.6.0
+      find-up-simple: 1.0.1
+      globals: 16.3.0
+      indent-string: 5.0.0
+      is-builtin-module: 5.0.0
+      jsesc: 3.1.0
+      pluralize: 8.0.0
+      regexp-tree: 0.1.27
+      regjsparser: 0.12.0
+      semver: 7.7.2
+      strip-indent: 4.0.0
+
+  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      eslint: 9.32.0(jiti@2.5.1)
+    optionalDependencies:
+      '@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+
+  eslint-plugin-vue@10.2.0(eslint@9.32.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.32.0(jiti@2.5.1))):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      eslint: 9.32.0(jiti@2.5.1)
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.1.2
+      semver: 7.7.2
+      vue-eslint-parser: 10.2.0(eslint@9.32.0(jiti@2.5.1))
+      xml-name-validator: 4.0.0
+
+  eslint-plugin-vue@10.4.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.32.0(jiti@2.5.1))):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      eslint: 9.32.0(jiti@2.5.1)
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.1.2
+      semver: 7.7.2
+      vue-eslint-parser: 10.2.0(eslint@9.32.0(jiti@2.5.1))
+      xml-name-validator: 4.0.0
+    optionalDependencies:
+      '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
+
+  eslint-plugin-yml@1.18.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      debug: 4.4.1
+      escape-string-regexp: 4.0.0
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-compat-utils: 0.6.5(eslint@9.32.0(jiti@2.5.1))
+      natural-compare: 1.4.0
+      yaml-eslint-parser: 1.3.0
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.18)(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      '@vue/compiler-sfc': 3.5.18
+      eslint: 9.32.0(jiti@2.5.1)
+
+  eslint-scope@8.4.0:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
+  eslint-visitor-keys@3.4.3: {}
+
+  eslint-visitor-keys@4.2.1: {}
+
+  eslint@9.32.0(jiti@2.5.1):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
+      '@eslint-community/regexpp': 4.12.1
+      '@eslint/config-array': 0.21.0
+      '@eslint/config-helpers': 0.3.0
+      '@eslint/core': 0.15.1
+      '@eslint/eslintrc': 3.3.1
+      '@eslint/js': 9.32.0
+      '@eslint/plugin-kit': 0.3.4
+      '@humanfs/node': 0.16.6
+      '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.4.3
+      '@types/estree': 1.0.8
+      '@types/json-schema': 7.0.15
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.6
+      debug: 4.4.1
+      escape-string-regexp: 4.0.0
+      eslint-scope: 8.4.0
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      esquery: 1.6.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 8.0.0
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      ignore: 5.3.2
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      json-stable-stringify-without-jsonify: 1.0.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.4
+    optionalDependencies:
+      jiti: 2.5.1
+    transitivePeerDependencies:
+      - supports-color
+
+  espree@10.4.0:
+    dependencies:
+      acorn: 8.15.0
+      acorn-jsx: 5.3.2(acorn@8.15.0)
+      eslint-visitor-keys: 4.2.1
+
+  espree@9.6.1:
+    dependencies:
+      acorn: 8.15.0
+      acorn-jsx: 5.3.2(acorn@8.15.0)
+      eslint-visitor-keys: 3.4.3
+
+  esquery@1.6.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  esrecurse@4.3.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  estraverse@5.3.0: {}
+
+  estree-walker@2.0.2: {}
+
+  estree-walker@3.0.3:
+    dependencies:
+      '@types/estree': 1.0.8
+
+  esutils@2.0.3: {}
+
+  eventemitter3@5.0.1: {}
+
+  evtd@0.2.4: {}
+
+  execa@5.1.1:
+    dependencies:
+      cross-spawn: 7.0.6
+      get-stream: 6.0.1
+      human-signals: 2.1.0
+      is-stream: 2.0.1
+      merge-stream: 2.0.0
+      npm-run-path: 4.0.1
+      onetime: 5.1.2
+      signal-exit: 3.0.7
+      strip-final-newline: 2.0.0
+
+  execa@9.6.0:
+    dependencies:
+      '@sindresorhus/merge-streams': 4.0.0
+      cross-spawn: 7.0.6
+      figures: 6.1.0
+      get-stream: 9.0.1
+      human-signals: 8.0.1
+      is-plain-obj: 4.1.0
+      is-stream: 4.0.1
+      npm-run-path: 6.0.0
+      pretty-ms: 9.2.0
+      signal-exit: 4.1.0
+      strip-final-newline: 4.0.0
+      yoctocolors: 2.1.1
+
+  expect-type@1.2.2: {}
+
+  exsolve@1.0.7: {}
+
+  fast-deep-equal@3.1.3: {}
+
+  fast-diff@1.3.0: {}
+
+  fast-glob@3.3.3:
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.8
+
+  fast-json-stable-stringify@2.1.0: {}
+
+  fast-levenshtein@2.0.6: {}
+
+  fast-npm-meta@0.4.6: {}
+
+  fastq@1.19.1:
+    dependencies:
+      reusify: 1.1.0
+
+  fault@2.0.1:
+    dependencies:
+      format: 0.2.2
+
+  fdir@6.4.6(picomatch@4.0.3):
+    optionalDependencies:
+      picomatch: 4.0.3
+
+  fecha@4.2.3: {}
+
+  fflate@0.8.2: {}
+
+  figures@6.1.0:
+    dependencies:
+      is-unicode-supported: 2.1.0
+
+  file-entry-cache@8.0.0:
+    dependencies:
+      flat-cache: 4.0.1
+
+  filelist@1.0.4:
+    dependencies:
+      minimatch: 5.1.6
+
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+
+  find-up-simple@1.0.1: {}
+
+  find-up@5.0.0:
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+
+  flat-cache@4.0.1:
+    dependencies:
+      flatted: 3.3.3
+      keyv: 4.5.4
+
+  flatted@3.3.3: {}
+
+  flru@1.0.2: {}
+
+  foreground-child@3.3.1:
+    dependencies:
+      cross-spawn: 7.0.6
+      signal-exit: 4.1.0
+
+  format@0.2.2: {}
+
+  fs-extra@10.1.0:
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+
+  fsevents@2.3.2:
+    optional: true
+
+  fsevents@2.3.3:
+    optional: true
+
+  gensync@1.0.0-beta.2: {}
+
+  get-port-please@3.2.0: {}
+
+  get-stream@6.0.1: {}
+
+  get-stream@9.0.1:
+    dependencies:
+      '@sec-ant/readable-stream': 0.4.1
+      is-stream: 4.0.1
+
+  get-tsconfig@4.10.1:
+    dependencies:
+      resolve-pkg-maps: 1.0.0
+
+  github-slugger@2.0.0: {}
+
+  gl-matrix@3.4.3: {}
+
+  glob-parent@5.1.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob-parent@6.0.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob@10.4.5:
+    dependencies:
+      foreground-child: 3.3.1
+      jackspeak: 3.4.3
+      minimatch: 9.0.5
+      minipass: 7.1.2
+      package-json-from-dist: 1.0.1
+      path-scurry: 1.11.1
+
+  globals@13.24.0:
+    dependencies:
+      type-fest: 0.20.2
+
+  globals@14.0.0: {}
+
+  globals@15.15.0: {}
+
+  globals@16.3.0: {}
+
+  globrex@0.1.2: {}
+
+  graceful-fs@4.2.11: {}
+
+  graphemer@1.4.0: {}
+
+  gzip-size@6.0.0:
+    dependencies:
+      duplexer: 0.1.2
+
+  h3@1.15.4:
+    dependencies:
+      cookie-es: 1.2.2
+      crossws: 0.3.5
+      defu: 6.1.4
+      destr: 2.0.5
+      iron-webcrypto: 1.2.1
+      node-mock-http: 1.0.2
+      radix3: 1.1.2
+      ufo: 1.6.1
+      uncrypto: 0.1.3
+
+  has-flag@4.0.0: {}
+
+  hash-sum@2.0.0: {}
+
+  he@1.2.0: {}
+
+  highlight.js@11.11.1: {}
+
+  hookable@5.5.3: {}
+
+  html-encoding-sniffer@4.0.0:
+    dependencies:
+      whatwg-encoding: 3.1.1
+
+  html-minifier-terser@6.1.0:
+    dependencies:
+      camel-case: 4.1.2
+      clean-css: 5.3.3
+      commander: 8.3.0
+      he: 1.2.0
+      param-case: 3.0.4
+      relateurl: 0.2.7
+      terser: 5.43.1
+
+  http-proxy-agent@7.0.2:
+    dependencies:
+      agent-base: 7.1.4
+      debug: 4.4.1
+    transitivePeerDependencies:
+      - supports-color
+
+  https-proxy-agent@7.0.6:
+    dependencies:
+      agent-base: 7.1.4
+      debug: 4.4.1
+    transitivePeerDependencies:
+      - supports-color
+
+  human-signals@2.1.0: {}
+
+  human-signals@8.0.1: {}
+
+  iconv-lite@0.6.3:
+    dependencies:
+      safer-buffer: 2.1.2
+
+  ignore@5.3.2: {}
+
+  ignore@7.0.5: {}
+
+  immutable@5.1.3: {}
+
+  import-fresh@3.3.1:
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+
+  imurmurhash@0.1.4: {}
+
+  indent-string@5.0.0: {}
+
+  ini@1.3.8: {}
+
+  internmap@2.0.3: {}
+
+  iron-webcrypto@1.2.1: {}
+
+  is-arrayish@0.3.2: {}
+
+  is-binary-path@2.1.0:
+    dependencies:
+      binary-extensions: 2.3.0
+
+  is-builtin-module@5.0.0:
+    dependencies:
+      builtin-modules: 5.0.0
+
+  is-docker@3.0.0: {}
+
+  is-extglob@2.1.1: {}
+
+  is-fullwidth-code-point@3.0.0: {}
+
+  is-glob@4.0.3:
+    dependencies:
+      is-extglob: 2.1.1
+
+  is-inside-container@1.0.0:
+    dependencies:
+      is-docker: 3.0.0
+
+  is-number@7.0.0: {}
+
+  is-plain-obj@4.1.0: {}
+
+  is-potential-custom-element-name@1.0.1: {}
+
+  is-stream@2.0.1: {}
+
+  is-stream@4.0.1: {}
+
+  is-unicode-supported@2.1.0: {}
+
+  is-what@4.1.16: {}
+
+  is-wsl@3.1.0:
+    dependencies:
+      is-inside-container: 1.0.0
+
+  isarray@1.0.0: {}
+
+  isexe@2.0.0: {}
+
+  isexe@3.1.1: {}
+
+  isobject@2.1.0:
+    dependencies:
+      isarray: 1.0.0
+
+  jackspeak@3.4.3:
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+
+  jake@10.9.4:
+    dependencies:
+      async: 3.2.6
+      filelist: 1.0.4
+      picocolors: 1.1.1
+
+  jiti@2.5.1: {}
+
+  js-beautify@1.15.4:
+    dependencies:
+      config-chain: 1.1.13
+      editorconfig: 1.0.4
+      glob: 10.4.5
+      js-cookie: 3.0.5
+      nopt: 7.2.1
+
+  js-cookie@3.0.5: {}
+
+  js-tokens@4.0.0: {}
+
+  js-tokens@9.0.1: {}
+
+  js-yaml@4.1.0:
+    dependencies:
+      argparse: 2.0.1
+
+  jsdoc-type-pratt-parser@4.1.0: {}
+
+  jsdom@26.1.0:
+    dependencies:
+      cssstyle: 4.6.0
+      data-urls: 5.0.0
+      decimal.js: 10.6.0
+      html-encoding-sniffer: 4.0.0
+      http-proxy-agent: 7.0.2
+      https-proxy-agent: 7.0.6
+      is-potential-custom-element-name: 1.0.1
+      nwsapi: 2.2.21
+      parse5: 7.3.0
+      rrweb-cssom: 0.8.0
+      saxes: 6.0.0
+      symbol-tree: 3.2.4
+      tough-cookie: 5.1.2
+      w3c-xmlserializer: 5.0.0
+      webidl-conversions: 7.0.0
+      whatwg-encoding: 3.1.1
+      whatwg-mimetype: 4.0.0
+      whatwg-url: 14.2.0
+      ws: 8.18.3
+      xml-name-validator: 5.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
+  jsesc@3.0.2: {}
+
+  jsesc@3.1.0: {}
+
+  json-buffer@3.0.1: {}
+
+  json-parse-even-better-errors@4.0.0: {}
+
+  json-schema-traverse@0.4.1: {}
+
+  json-stable-stringify-without-jsonify@1.0.1: {}
+
+  json5@2.2.3: {}
+
+  jsonc-eslint-parser@2.4.0:
+    dependencies:
+      acorn: 8.15.0
+      eslint-visitor-keys: 3.4.3
+      espree: 9.6.1
+      semver: 7.7.2
+
+  jsonfile@6.1.0:
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+
+  keyv@4.5.4:
+    dependencies:
+      json-buffer: 3.0.1
+
+  kolorist@1.8.0: {}
+
+  launch-editor@2.11.0:
+    dependencies:
+      picocolors: 1.1.1
+      shell-quote: 1.8.3
+
+  levn@0.4.1:
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+
+  lightningcss-darwin-arm64@1.30.1:
+    optional: true
+
+  lightningcss-darwin-x64@1.30.1:
+    optional: true
+
+  lightningcss-freebsd-x64@1.30.1:
+    optional: true
+
+  lightningcss-linux-arm-gnueabihf@1.30.1:
+    optional: true
+
+  lightningcss-linux-arm64-gnu@1.30.1:
+    optional: true
+
+  lightningcss-linux-arm64-musl@1.30.1:
+    optional: true
+
+  lightningcss-linux-x64-gnu@1.30.1:
+    optional: true
+
+  lightningcss-linux-x64-musl@1.30.1:
+    optional: true
+
+  lightningcss-win32-arm64-msvc@1.30.1:
+    optional: true
+
+  lightningcss-win32-x64-msvc@1.30.1:
+    optional: true
+
+  lightningcss@1.30.1:
+    dependencies:
+      detect-libc: 2.0.4
+    optionalDependencies:
+      lightningcss-darwin-arm64: 1.30.1
+      lightningcss-darwin-x64: 1.30.1
+      lightningcss-freebsd-x64: 1.30.1
+      lightningcss-linux-arm-gnueabihf: 1.30.1
+      lightningcss-linux-arm64-gnu: 1.30.1
+      lightningcss-linux-arm64-musl: 1.30.1
+      lightningcss-linux-x64-gnu: 1.30.1
+      lightningcss-linux-x64-musl: 1.30.1
+      lightningcss-win32-arm64-msvc: 1.30.1
+      lightningcss-win32-x64-msvc: 1.30.1
+
+  line-column@1.0.2:
+    dependencies:
+      isarray: 1.0.0
+      isobject: 2.1.0
+
+  local-pkg@1.1.1:
+    dependencies:
+      mlly: 1.7.4
+      pkg-types: 2.2.0
+      quansync: 0.2.10
+
+  locate-path@6.0.0:
+    dependencies:
+      p-locate: 5.0.0
+
+  lodash-es@4.17.21: {}
+
+  lodash.merge@4.6.2: {}
+
+  lodash@4.17.21: {}
+
+  longest-streak@3.1.0: {}
+
+  loupe@3.2.0: {}
+
+  lower-case@2.0.2:
+    dependencies:
+      tslib: 2.8.1
+
+  lru-cache@10.4.3: {}
+
+  lru-cache@11.1.0: {}
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  magic-string-ast@1.0.0:
+    dependencies:
+      magic-string: 0.30.17
+
+  magic-string@0.30.17:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.4
+
+  markdown-table@3.0.4: {}
+
+  mdast-util-find-and-replace@3.0.2:
+    dependencies:
+      '@types/mdast': 4.0.4
+      escape-string-regexp: 5.0.0
+      unist-util-is: 6.0.0
+      unist-util-visit-parents: 6.0.1
+
+  mdast-util-from-markdown@2.0.2:
+    dependencies:
+      '@types/mdast': 4.0.4
+      '@types/unist': 3.0.3
+      decode-named-character-reference: 1.2.0
+      devlop: 1.1.0
+      mdast-util-to-string: 4.0.0
+      micromark: 4.0.2
+      micromark-util-decode-numeric-character-reference: 2.0.2
+      micromark-util-decode-string: 2.0.1
+      micromark-util-normalize-identifier: 2.0.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+      unist-util-stringify-position: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-frontmatter@2.0.1:
+    dependencies:
+      '@types/mdast': 4.0.4
+      devlop: 1.1.0
+      escape-string-regexp: 5.0.0
+      mdast-util-from-markdown: 2.0.2
+      mdast-util-to-markdown: 2.1.2
+      micromark-extension-frontmatter: 2.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-gfm-autolink-literal@2.0.1:
+    dependencies:
+      '@types/mdast': 4.0.4
+      ccount: 2.0.1
+      devlop: 1.1.0
+      mdast-util-find-and-replace: 3.0.2
+      micromark-util-character: 2.1.1
+
+  mdast-util-gfm-footnote@2.1.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      devlop: 1.1.0
+      mdast-util-from-markdown: 2.0.2
+      mdast-util-to-markdown: 2.1.2
+      micromark-util-normalize-identifier: 2.0.1
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-gfm-strikethrough@2.0.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      mdast-util-from-markdown: 2.0.2
+      mdast-util-to-markdown: 2.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-gfm-table@2.0.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      devlop: 1.1.0
+      markdown-table: 3.0.4
+      mdast-util-from-markdown: 2.0.2
+      mdast-util-to-markdown: 2.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-gfm-task-list-item@2.0.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      devlop: 1.1.0
+      mdast-util-from-markdown: 2.0.2
+      mdast-util-to-markdown: 2.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-gfm@3.1.0:
+    dependencies:
+      mdast-util-from-markdown: 2.0.2
+      mdast-util-gfm-autolink-literal: 2.0.1
+      mdast-util-gfm-footnote: 2.1.0
+      mdast-util-gfm-strikethrough: 2.0.0
+      mdast-util-gfm-table: 2.0.0
+      mdast-util-gfm-task-list-item: 2.0.0
+      mdast-util-to-markdown: 2.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-phrasing@4.1.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      unist-util-is: 6.0.0
+
+  mdast-util-to-markdown@2.1.2:
+    dependencies:
+      '@types/mdast': 4.0.4
+      '@types/unist': 3.0.3
+      longest-streak: 3.1.0
+      mdast-util-phrasing: 4.1.0
+      mdast-util-to-string: 4.0.0
+      micromark-util-classify-character: 2.0.1
+      micromark-util-decode-string: 2.0.1
+      unist-util-visit: 5.0.0
+      zwitch: 2.0.4
+
+  mdast-util-to-string@4.0.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+
+  mdn-data@2.12.2: {}
+
+  memorystream@0.3.1: {}
+
+  merge-source-map@1.1.0:
+    dependencies:
+      source-map: 0.6.1
+
+  merge-stream@2.0.0: {}
+
+  merge2@1.4.1: {}
+
+  micromark-core-commonmark@2.0.3:
+    dependencies:
+      decode-named-character-reference: 1.2.0
+      devlop: 1.1.0
+      micromark-factory-destination: 2.0.1
+      micromark-factory-label: 2.0.1
+      micromark-factory-space: 2.0.1
+      micromark-factory-title: 2.0.1
+      micromark-factory-whitespace: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-chunked: 2.0.1
+      micromark-util-classify-character: 2.0.1
+      micromark-util-html-tag-name: 2.0.1
+      micromark-util-normalize-identifier: 2.0.1
+      micromark-util-resolve-all: 2.0.1
+      micromark-util-subtokenize: 2.1.0
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-extension-frontmatter@2.0.0:
+    dependencies:
+      fault: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-extension-gfm-autolink-literal@2.1.0:
+    dependencies:
+      micromark-util-character: 2.1.1
+      micromark-util-sanitize-uri: 2.0.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-extension-gfm-footnote@2.1.0:
+    dependencies:
+      devlop: 1.1.0
+      micromark-core-commonmark: 2.0.3
+      micromark-factory-space: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-normalize-identifier: 2.0.1
+      micromark-util-sanitize-uri: 2.0.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-extension-gfm-strikethrough@2.1.0:
+    dependencies:
+      devlop: 1.1.0
+      micromark-util-chunked: 2.0.1
+      micromark-util-classify-character: 2.0.1
+      micromark-util-resolve-all: 2.0.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-extension-gfm-table@2.1.1:
+    dependencies:
+      devlop: 1.1.0
+      micromark-factory-space: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-extension-gfm-tagfilter@2.0.0:
+    dependencies:
+      micromark-util-types: 2.0.2
+
+  micromark-extension-gfm-task-list-item@2.1.0:
+    dependencies:
+      devlop: 1.1.0
+      micromark-factory-space: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-extension-gfm@3.0.0:
+    dependencies:
+      micromark-extension-gfm-autolink-literal: 2.1.0
+      micromark-extension-gfm-footnote: 2.1.0
+      micromark-extension-gfm-strikethrough: 2.1.0
+      micromark-extension-gfm-table: 2.1.1
+      micromark-extension-gfm-tagfilter: 2.0.0
+      micromark-extension-gfm-task-list-item: 2.1.0
+      micromark-util-combine-extensions: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-factory-destination@2.0.1:
+    dependencies:
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-factory-label@2.0.1:
+    dependencies:
+      devlop: 1.1.0
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-factory-space@2.0.1:
+    dependencies:
+      micromark-util-character: 2.1.1
+      micromark-util-types: 2.0.2
+
+  micromark-factory-title@2.0.1:
+    dependencies:
+      micromark-factory-space: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-factory-whitespace@2.0.1:
+    dependencies:
+      micromark-factory-space: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-util-character@2.1.1:
+    dependencies:
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-util-chunked@2.0.1:
+    dependencies:
+      micromark-util-symbol: 2.0.1
+
+  micromark-util-classify-character@2.0.1:
+    dependencies:
+      micromark-util-character: 2.1.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-util-combine-extensions@2.0.1:
+    dependencies:
+      micromark-util-chunked: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-util-decode-numeric-character-reference@2.0.2:
+    dependencies:
+      micromark-util-symbol: 2.0.1
+
+  micromark-util-decode-string@2.0.1:
+    dependencies:
+      decode-named-character-reference: 1.2.0
+      micromark-util-character: 2.1.1
+      micromark-util-decode-numeric-character-reference: 2.0.2
+      micromark-util-symbol: 2.0.1
+
+  micromark-util-encode@2.0.1: {}
+
+  micromark-util-html-tag-name@2.0.1: {}
+
+  micromark-util-normalize-identifier@2.0.1:
+    dependencies:
+      micromark-util-symbol: 2.0.1
+
+  micromark-util-resolve-all@2.0.1:
+    dependencies:
+      micromark-util-types: 2.0.2
+
+  micromark-util-sanitize-uri@2.0.1:
+    dependencies:
+      micromark-util-character: 2.1.1
+      micromark-util-encode: 2.0.1
+      micromark-util-symbol: 2.0.1
+
+  micromark-util-subtokenize@2.1.0:
+    dependencies:
+      devlop: 1.1.0
+      micromark-util-chunked: 2.0.1
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+
+  micromark-util-symbol@2.0.1: {}
+
+  micromark-util-types@2.0.2: {}
+
+  micromark@4.0.2:
+    dependencies:
+      '@types/debug': 4.1.12
+      debug: 4.4.1
+      decode-named-character-reference: 1.2.0
+      devlop: 1.1.0
+      micromark-core-commonmark: 2.0.3
+      micromark-factory-space: 2.0.1
+      micromark-util-character: 2.1.1
+      micromark-util-chunked: 2.0.1
+      micromark-util-combine-extensions: 2.0.1
+      micromark-util-decode-numeric-character-reference: 2.0.2
+      micromark-util-encode: 2.0.1
+      micromark-util-normalize-identifier: 2.0.1
+      micromark-util-resolve-all: 2.0.1
+      micromark-util-sanitize-uri: 2.0.1
+      micromark-util-subtokenize: 2.1.0
+      micromark-util-symbol: 2.0.1
+      micromark-util-types: 2.0.2
+    transitivePeerDependencies:
+      - supports-color
+
+  micromatch@4.0.8:
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.1
+
+  mimic-fn@2.1.0: {}
+
+  min-indent@1.0.1: {}
+
+  minimatch@10.0.3:
+    dependencies:
+      '@isaacs/brace-expansion': 5.0.0
+
+  minimatch@3.1.2:
+    dependencies:
+      brace-expansion: 1.1.12
+
+  minimatch@5.1.6:
+    dependencies:
+      brace-expansion: 2.0.2
+
+  minimatch@9.0.1:
+    dependencies:
+      brace-expansion: 2.0.2
+
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.2
+
+  minipass@7.1.2: {}
+
+  mitt@3.0.1: {}
+
+  mlly@1.7.4:
+    dependencies:
+      acorn: 8.15.0
+      pathe: 2.0.3
+      pkg-types: 1.3.1
+      ufo: 1.6.1
+
+  mri@1.2.0: {}
+
+  mrmime@2.0.1: {}
+
+  ms@2.1.3: {}
+
+  muggle-string@0.4.1: {}
+
+  naive-ui@2.42.0(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      '@css-render/plugin-bem': 0.15.14(css-render@0.15.14)
+      '@css-render/vue3-ssr': 0.15.14(vue@3.5.18(typescript@5.8.3))
+      '@types/katex': 0.16.7
+      '@types/lodash': 4.17.20
+      '@types/lodash-es': 4.17.12
+      async-validator: 4.2.5
+      css-render: 0.15.14
+      csstype: 3.1.3
+      date-fns: 3.6.0
+      date-fns-tz: 3.2.0(date-fns@3.6.0)
+      evtd: 0.2.4
+      highlight.js: 11.11.1
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      seemly: 0.3.10
+      treemate: 0.3.11
+      vdirs: 0.1.8(vue@3.5.18(typescript@5.8.3))
+      vooks: 0.2.12(vue@3.5.18(typescript@5.8.3))
+      vue: 3.5.18(typescript@5.8.3)
+      vueuc: 0.4.64(vue@3.5.18(typescript@5.8.3))
+
+  nanoid@3.3.11: {}
+
+  nanoid@5.1.5: {}
+
+  napi-postinstall@0.3.2: {}
+
+  natural-compare@1.4.0: {}
+
+  natural-orderby@5.0.0: {}
+
+  neverthrow@8.2.0:
+    optionalDependencies:
+      '@rollup/rollup-linux-x64-gnu': 4.46.2
+
+  no-case@3.0.4:
+    dependencies:
+      lower-case: 2.0.2
+      tslib: 2.8.1
+
+  node-addon-api@7.1.1:
+    optional: true
+
+  node-fetch-native@1.6.7: {}
+
+  node-html-parser@5.4.2:
+    dependencies:
+      css-select: 4.3.0
+      he: 1.2.0
+
+  node-mock-http@1.0.2: {}
+
+  node-modules-inspector@1.0.0:
+    dependencies:
+      ansis: 4.1.0
+      birpc: 2.5.0
+      cac: 6.7.14
+      fast-npm-meta: 0.4.6
+      get-port-please: 3.2.0
+      h3: 1.15.4
+      launch-editor: 2.11.0
+      mlly: 1.7.4
+      mrmime: 2.0.1
+      node-modules-tools: 1.0.0
+      ohash: 2.0.11
+      open: 10.2.0
+      p-limit: 6.2.0
+      pathe: 2.0.3
+      publint: 0.3.12
+      structured-clone-es: 1.0.0
+      tinyglobby: 0.2.14
+      unconfig: 7.3.2
+      unstorage: 1.16.1
+      ws: 8.18.3
+    transitivePeerDependencies:
+      - '@azure/app-configuration'
+      - '@azure/cosmos'
+      - '@azure/data-tables'
+      - '@azure/identity'
+      - '@azure/keyvault-secrets'
+      - '@azure/storage-blob'
+      - '@capacitor/preferences'
+      - '@deno/kv'
+      - '@netlify/blobs'
+      - '@planetscale/database'
+      - '@upstash/redis'
+      - '@vercel/blob'
+      - '@vercel/kv'
+      - aws4fetch
+      - bufferutil
+      - db0
+      - idb-keyval
+      - ioredis
+      - uploadthing
+      - utf-8-validate
+
+  node-modules-tools@1.0.0:
+    dependencies:
+      js-yaml: 4.1.0
+      p-limit: 6.2.0
+      package-manager-detector: 1.3.0
+      pathe: 2.0.3
+      pkg-types: 2.2.0
+      publint: 0.3.12
+      semver: 7.7.2
+      tinyexec: 1.0.1
+
+  node-releases@2.0.19: {}
+
+  nopt@7.2.1:
+    dependencies:
+      abbrev: 2.0.0
+
+  normalize-path@3.0.0: {}
+
+  npm-normalize-package-bin@4.0.0: {}
+
+  npm-run-all2@8.0.4:
+    dependencies:
+      ansi-styles: 6.2.1
+      cross-spawn: 7.0.6
+      memorystream: 0.3.1
+      picomatch: 4.0.3
+      pidtree: 0.6.0
+      read-package-json-fast: 4.0.0
+      shell-quote: 1.8.3
+      which: 5.0.0
+
+  npm-run-path@4.0.1:
+    dependencies:
+      path-key: 3.1.1
+
+  npm-run-path@6.0.0:
+    dependencies:
+      path-key: 4.0.0
+      unicorn-magic: 0.3.0
+
+  nth-check@2.1.1:
+    dependencies:
+      boolbase: 1.0.0
+
+  nwsapi@2.2.21: {}
+
+  object-treeify@1.1.33: {}
+
+  ofetch@1.4.1:
+    dependencies:
+      destr: 2.0.5
+      node-fetch-native: 1.6.7
+      ufo: 1.6.1
+
+  ohash@2.0.11: {}
+
+  onetime@5.1.2:
+    dependencies:
+      mimic-fn: 2.1.0
+
+  open@10.2.0:
+    dependencies:
+      default-browser: 5.2.1
+      define-lazy-prop: 3.0.0
+      is-inside-container: 1.0.0
+      wsl-utils: 0.1.0
+
+  optionator@0.9.4:
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.5
+
+  oxc-resolver@11.6.1:
+    dependencies:
+      napi-postinstall: 0.3.2
+    optionalDependencies:
+      '@oxc-resolver/binding-android-arm-eabi': 11.6.1
+      '@oxc-resolver/binding-android-arm64': 11.6.1
+      '@oxc-resolver/binding-darwin-arm64': 11.6.1
+      '@oxc-resolver/binding-darwin-x64': 11.6.1
+      '@oxc-resolver/binding-freebsd-x64': 11.6.1
+      '@oxc-resolver/binding-linux-arm-gnueabihf': 11.6.1
+      '@oxc-resolver/binding-linux-arm-musleabihf': 11.6.1
+      '@oxc-resolver/binding-linux-arm64-gnu': 11.6.1
+      '@oxc-resolver/binding-linux-arm64-musl': 11.6.1
+      '@oxc-resolver/binding-linux-ppc64-gnu': 11.6.1
+      '@oxc-resolver/binding-linux-riscv64-gnu': 11.6.1
+      '@oxc-resolver/binding-linux-riscv64-musl': 11.6.1
+      '@oxc-resolver/binding-linux-s390x-gnu': 11.6.1
+      '@oxc-resolver/binding-linux-x64-gnu': 11.6.1
+      '@oxc-resolver/binding-linux-x64-musl': 11.6.1
+      '@oxc-resolver/binding-wasm32-wasi': 11.6.1
+      '@oxc-resolver/binding-win32-arm64-msvc': 11.6.1
+      '@oxc-resolver/binding-win32-ia32-msvc': 11.6.1
+      '@oxc-resolver/binding-win32-x64-msvc': 11.6.1
+
+  p-limit@3.1.0:
+    dependencies:
+      yocto-queue: 0.1.0
+
+  p-limit@6.2.0:
+    dependencies:
+      yocto-queue: 1.2.1
+
+  p-locate@5.0.0:
+    dependencies:
+      p-limit: 3.1.0
+
+  package-json-from-dist@1.0.1: {}
+
+  package-manager-detector@1.3.0: {}
+
+  param-case@3.0.4:
+    dependencies:
+      dot-case: 3.0.4
+      tslib: 2.8.1
+
+  parent-module@1.0.1:
+    dependencies:
+      callsites: 3.1.0
+
+  parse-gitignore@2.0.0: {}
+
+  parse-imports-exports@0.2.4:
+    dependencies:
+      parse-statements: 1.0.11
+
+  parse-ms@4.0.0: {}
+
+  parse-statements@1.0.11: {}
+
+  parse5@7.3.0:
+    dependencies:
+      entities: 6.0.1
+
+  pascal-case@3.1.2:
+    dependencies:
+      no-case: 3.0.4
+      tslib: 2.8.1
+
+  path-browserify@1.0.1: {}
+
+  path-exists@4.0.0: {}
+
+  path-key@3.1.1: {}
+
+  path-key@4.0.0: {}
+
+  path-scurry@1.11.1:
+    dependencies:
+      lru-cache: 10.4.3
+      minipass: 7.1.2
+
+  pathe@0.2.0: {}
+
+  pathe@2.0.3: {}
+
+  pathval@2.0.1: {}
+
+  pdfast@0.2.0: {}
+
+  perfect-debounce@1.0.0: {}
+
+  picocolors@1.1.1: {}
+
+  picomatch@2.3.1: {}
+
+  picomatch@4.0.3: {}
+
+  pidtree@0.6.0: {}
+
+  pinia-plugin-persistedstate@4.4.1(pinia@3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))):
+    dependencies:
+      deep-pick-omit: 1.2.1
+      defu: 6.1.4
+      destr: 2.0.5
+    optionalDependencies:
+      pinia: 3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3))
+
+  pinia@3.0.3(typescript@5.8.3)(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      '@vue/devtools-api': 7.7.7
+      vue: 3.5.18(typescript@5.8.3)
+    optionalDependencies:
+      typescript: 5.8.3
+
+  pkg-types@1.3.1:
+    dependencies:
+      confbox: 0.1.8
+      mlly: 1.7.4
+      pathe: 2.0.3
+
+  pkg-types@2.2.0:
+    dependencies:
+      confbox: 0.2.2
+      exsolve: 1.0.7
+      pathe: 2.0.3
+
+  playwright-core@1.54.2: {}
+
+  playwright@1.54.2:
+    dependencies:
+      playwright-core: 1.54.2
+    optionalDependencies:
+      fsevents: 2.3.2
+
+  pluralize@8.0.0: {}
+
+  pnpm-workspace-yaml@1.1.0:
+    dependencies:
+      yaml: 2.8.1
+
+  postcss-selector-parser@6.1.2:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss-selector-parser@7.1.0:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss@8.5.6:
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  prelude-ls@1.2.1: {}
+
+  prettier-linter-helpers@1.0.0:
+    dependencies:
+      fast-diff: 1.3.0
+
+  prettier@3.5.3: {}
+
+  pretty-ms@9.2.0:
+    dependencies:
+      parse-ms: 4.0.0
+
+  proto-list@1.2.4: {}
+
+  publint@0.3.12:
+    dependencies:
+      '@publint/pack': 0.1.2
+      package-manager-detector: 1.3.0
+      picocolors: 1.1.1
+      sade: 1.8.1
+
+  punycode@2.3.1: {}
+
+  quansync@0.2.10: {}
+
+  queue-microtask@1.2.3: {}
+
+  quickselect@2.0.0: {}
+
+  radix3@1.1.2: {}
+
+  rbush@3.0.1:
+    dependencies:
+      quickselect: 2.0.0
+
+  read-package-json-fast@4.0.0:
+    dependencies:
+      json-parse-even-better-errors: 4.0.0
+      npm-normalize-package-bin: 4.0.0
+
+  readdirp@3.6.0:
+    dependencies:
+      picomatch: 2.3.1
+
+  readdirp@4.1.2: {}
+
+  refa@0.12.1:
+    dependencies:
+      '@eslint-community/regexpp': 4.12.1
+
+  regexp-ast-analysis@0.7.1:
+    dependencies:
+      '@eslint-community/regexpp': 4.12.1
+      refa: 0.12.1
+
+  regexp-tree@0.1.27: {}
+
+  regjsparser@0.12.0:
+    dependencies:
+      jsesc: 3.0.2
+
+  relateurl@0.2.7: {}
+
+  request-light@0.7.0: {}
+
+  resolve-from@4.0.0: {}
+
+  resolve-pkg-maps@1.0.0: {}
+
+  reusify@1.1.0: {}
+
+  rfdc@1.4.1: {}
+
+  rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1):
+    dependencies:
+      fdir: 6.4.6(picomatch@4.0.3)
+      lightningcss: 1.30.1
+      picomatch: 4.0.3
+      postcss: 8.5.6
+      rolldown: 1.0.0-beta.31
+      tinyglobby: 0.2.14
+    optionalDependencies:
+      '@types/node': 22.17.0
+      esbuild: 0.25.8
+      fsevents: 2.3.3
+      jiti: 2.5.1
+      sass: 1.90.0
+      sass-embedded: 1.90.0
+      terser: 5.43.1
+      yaml: 2.8.1
+
+  rolldown@1.0.0-beta.31:
+    dependencies:
+      '@oxc-project/runtime': 0.80.0
+      '@oxc-project/types': 0.80.0
+      '@rolldown/pluginutils': 1.0.0-beta.31
+      ansis: 4.1.0
+    optionalDependencies:
+      '@rolldown/binding-android-arm64': 1.0.0-beta.31
+      '@rolldown/binding-darwin-arm64': 1.0.0-beta.31
+      '@rolldown/binding-darwin-x64': 1.0.0-beta.31
+      '@rolldown/binding-freebsd-x64': 1.0.0-beta.31
+      '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.31
+      '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.31
+      '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.31
+      '@rolldown/binding-linux-arm64-ohos': 1.0.0-beta.31
+      '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.31
+      '@rolldown/binding-linux-x64-musl': 1.0.0-beta.31
+      '@rolldown/binding-wasm32-wasi': 1.0.0-beta.31
+      '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.31
+      '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.31
+      '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.31
+
+  rollup@4.46.2:
+    dependencies:
+      '@types/estree': 1.0.8
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.46.2
+      '@rollup/rollup-android-arm64': 4.46.2
+      '@rollup/rollup-darwin-arm64': 4.46.2
+      '@rollup/rollup-darwin-x64': 4.46.2
+      '@rollup/rollup-freebsd-arm64': 4.46.2
+      '@rollup/rollup-freebsd-x64': 4.46.2
+      '@rollup/rollup-linux-arm-gnueabihf': 4.46.2
+      '@rollup/rollup-linux-arm-musleabihf': 4.46.2
+      '@rollup/rollup-linux-arm64-gnu': 4.46.2
+      '@rollup/rollup-linux-arm64-musl': 4.46.2
+      '@rollup/rollup-linux-loongarch64-gnu': 4.46.2
+      '@rollup/rollup-linux-ppc64-gnu': 4.46.2
+      '@rollup/rollup-linux-riscv64-gnu': 4.46.2
+      '@rollup/rollup-linux-riscv64-musl': 4.46.2
+      '@rollup/rollup-linux-s390x-gnu': 4.46.2
+      '@rollup/rollup-linux-x64-gnu': 4.46.2
+      '@rollup/rollup-linux-x64-musl': 4.46.2
+      '@rollup/rollup-win32-arm64-msvc': 4.46.2
+      '@rollup/rollup-win32-ia32-msvc': 4.46.2
+      '@rollup/rollup-win32-x64-msvc': 4.46.2
+      fsevents: 2.3.3
+
+  rrweb-cssom@0.8.0: {}
+
+  run-applescript@7.0.0: {}
+
+  run-parallel@1.2.0:
+    dependencies:
+      queue-microtask: 1.2.3
+
+  rw@1.3.3: {}
+
+  rxjs@7.8.2:
+    dependencies:
+      tslib: 2.8.1
+
+  sade@1.8.1:
+    dependencies:
+      mri: 1.2.0
+
+  safer-buffer@2.1.2: {}
+
+  sass-embedded-all-unknown@1.90.0:
+    dependencies:
+      sass: 1.90.0
+    optional: true
+
+  sass-embedded-android-arm64@1.90.0:
+    optional: true
+
+  sass-embedded-android-arm@1.90.0:
+    optional: true
+
+  sass-embedded-android-riscv64@1.90.0:
+    optional: true
+
+  sass-embedded-android-x64@1.90.0:
+    optional: true
+
+  sass-embedded-darwin-arm64@1.90.0:
+    optional: true
+
+  sass-embedded-darwin-x64@1.90.0:
+    optional: true
+
+  sass-embedded-linux-arm64@1.90.0:
+    optional: true
+
+  sass-embedded-linux-arm@1.90.0:
+    optional: true
+
+  sass-embedded-linux-musl-arm64@1.90.0:
+    optional: true
+
+  sass-embedded-linux-musl-arm@1.90.0:
+    optional: true
+
+  sass-embedded-linux-musl-riscv64@1.90.0:
+    optional: true
+
+  sass-embedded-linux-musl-x64@1.90.0:
+    optional: true
+
+  sass-embedded-linux-riscv64@1.90.0:
+    optional: true
+
+  sass-embedded-linux-x64@1.90.0:
+    optional: true
+
+  sass-embedded-unknown-all@1.90.0:
+    dependencies:
+      sass: 1.90.0
+    optional: true
+
+  sass-embedded-win32-arm64@1.90.0:
+    optional: true
+
+  sass-embedded-win32-x64@1.90.0:
+    optional: true
+
+  sass-embedded@1.90.0:
+    dependencies:
+      '@bufbuild/protobuf': 2.6.3
+      buffer-builder: 0.2.0
+      colorjs.io: 0.5.2
+      immutable: 5.1.3
+      rxjs: 7.8.2
+      supports-color: 8.1.1
+      sync-child-process: 1.0.2
+      varint: 6.0.0
+    optionalDependencies:
+      sass-embedded-all-unknown: 1.90.0
+      sass-embedded-android-arm: 1.90.0
+      sass-embedded-android-arm64: 1.90.0
+      sass-embedded-android-riscv64: 1.90.0
+      sass-embedded-android-x64: 1.90.0
+      sass-embedded-darwin-arm64: 1.90.0
+      sass-embedded-darwin-x64: 1.90.0
+      sass-embedded-linux-arm: 1.90.0
+      sass-embedded-linux-arm64: 1.90.0
+      sass-embedded-linux-musl-arm: 1.90.0
+      sass-embedded-linux-musl-arm64: 1.90.0
+      sass-embedded-linux-musl-riscv64: 1.90.0
+      sass-embedded-linux-musl-x64: 1.90.0
+      sass-embedded-linux-riscv64: 1.90.0
+      sass-embedded-linux-x64: 1.90.0
+      sass-embedded-unknown-all: 1.90.0
+      sass-embedded-win32-arm64: 1.90.0
+      sass-embedded-win32-x64: 1.90.0
+
+  sass@1.90.0:
+    dependencies:
+      chokidar: 4.0.3
+      immutable: 5.1.3
+      source-map-js: 1.2.1
+    optionalDependencies:
+      '@parcel/watcher': 2.5.1
+    optional: true
+
+  saxes@6.0.0:
+    dependencies:
+      xmlchars: 2.2.0
+
+  scslre@0.3.0:
+    dependencies:
+      '@eslint-community/regexpp': 4.12.1
+      refa: 0.12.1
+      regexp-ast-analysis: 0.7.1
+
+  scule@1.3.0: {}
+
+  seemly@0.3.10: {}
+
+  semver@6.3.1: {}
+
+  semver@7.7.2: {}
+
+  shebang-command@2.0.0:
+    dependencies:
+      shebang-regex: 3.0.0
+
+  shebang-regex@3.0.0: {}
+
+  shell-quote@1.8.3: {}
+
+  siginfo@2.0.0: {}
+
+  signal-exit@3.0.7: {}
+
+  signal-exit@4.1.0: {}
+
+  simple-swizzle@0.2.2:
+    dependencies:
+      is-arrayish: 0.3.2
+
+  sirv@3.0.1:
+    dependencies:
+      '@polka/url': 1.0.0-next.29
+      mrmime: 2.0.1
+      totalist: 3.0.1
+
+  sisteransi@1.0.5: {}
+
+  source-map-js@1.2.1: {}
+
+  source-map-support@0.5.21:
+    dependencies:
+      buffer-from: 1.1.2
+      source-map: 0.6.1
+
+  source-map@0.6.1: {}
+
+  spdx-exceptions@2.5.0: {}
+
+  spdx-expression-parse@4.0.0:
+    dependencies:
+      spdx-exceptions: 2.5.0
+      spdx-license-ids: 3.0.22
+
+  spdx-license-ids@3.0.22: {}
+
+  speakingurl@14.0.1: {}
+
+  stackback@0.0.2: {}
+
+  std-env@3.9.0: {}
+
+  string-width@4.2.3:
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+
+  string-width@5.1.2:
+    dependencies:
+      eastasianwidth: 0.2.0
+      emoji-regex: 9.2.2
+      strip-ansi: 7.1.0
+
+  strip-ansi@6.0.1:
+    dependencies:
+      ansi-regex: 5.0.1
+
+  strip-ansi@7.1.0:
+    dependencies:
+      ansi-regex: 6.1.0
+
+  strip-final-newline@2.0.0: {}
+
+  strip-final-newline@4.0.0: {}
+
+  strip-indent@4.0.0:
+    dependencies:
+      min-indent: 1.0.1
+
+  strip-json-comments@3.1.1: {}
+
+  strip-literal@3.0.0:
+    dependencies:
+      js-tokens: 9.0.1
+
+  structured-clone-es@1.0.0: {}
+
+  superjson@2.2.2:
+    dependencies:
+      copy-anything: 3.0.5
+
+  supports-color@7.2.0:
+    dependencies:
+      has-flag: 4.0.0
+
+  supports-color@8.1.1:
+    dependencies:
+      has-flag: 4.0.0
+
+  svg-path-parser@1.1.0: {}
+
+  symbol-tree@3.2.4: {}
+
+  sync-child-process@1.0.2:
+    dependencies:
+      sync-message-port: 1.1.3
+
+  sync-message-port@1.1.3: {}
+
+  synckit@0.11.11:
+    dependencies:
+      '@pkgr/core': 0.2.9
+
+  tapable@2.2.2: {}
+
+  terser@5.43.1:
+    dependencies:
+      '@jridgewell/source-map': 0.3.10
+      acorn: 8.15.0
+      commander: 2.20.3
+      source-map-support: 0.5.21
+
+  tinybench@2.9.0: {}
+
+  tinyexec@0.3.2: {}
+
+  tinyexec@1.0.1: {}
+
+  tinyglobby@0.2.14:
+    dependencies:
+      fdir: 6.4.6(picomatch@4.0.3)
+      picomatch: 4.0.3
+
+  tinypool@1.1.1: {}
+
+  tinyrainbow@2.0.0: {}
+
+  tinyspy@4.0.3: {}
+
+  tldts-core@6.1.86: {}
+
+  tldts@6.1.86:
+    dependencies:
+      tldts-core: 6.1.86
+
+  to-regex-range@5.0.1:
+    dependencies:
+      is-number: 7.0.0
+
+  toml-eslint-parser@0.10.0:
+    dependencies:
+      eslint-visitor-keys: 3.4.3
+
+  totalist@3.0.1: {}
+
+  tough-cookie@5.1.2:
+    dependencies:
+      tldts: 6.1.86
+
+  tr46@5.1.1:
+    dependencies:
+      punycode: 2.3.1
+
+  treemate@0.3.11: {}
+
+  ts-api-utils@2.1.0(typescript@5.8.3):
+    dependencies:
+      typescript: 5.8.3
+
+  ts-declaration-location@1.0.7(typescript@5.8.3):
+    dependencies:
+      picomatch: 4.0.3
+      typescript: 5.8.3
+
+  ts-macro@0.3.2:
+    dependencies:
+      muggle-string: 0.4.1
+
+  ts-morph@26.0.0:
+    dependencies:
+      '@ts-morph/common': 0.27.0
+      code-block-writer: 13.0.3
+
+  tslib@2.8.1: {}
+
+  type-check@0.4.0:
+    dependencies:
+      prelude-ls: 1.2.1
+
+  type-fest@0.20.2: {}
+
+  typescript@5.8.3: {}
+
+  ufo@1.6.1: {}
+
+  unconfig@7.3.2:
+    dependencies:
+      '@quansync/fs': 0.1.3
+      defu: 6.1.4
+      jiti: 2.5.1
+      quansync: 0.2.10
+
+  uncrypto@0.1.3: {}
+
+  undici-types@6.21.0: {}
+
+  unicorn-magic@0.3.0: {}
+
+  unimport@5.2.0:
+    dependencies:
+      acorn: 8.15.0
+      escape-string-regexp: 5.0.0
+      estree-walker: 3.0.3
+      local-pkg: 1.1.1
+      magic-string: 0.30.17
+      mlly: 1.7.4
+      pathe: 2.0.3
+      picomatch: 4.0.3
+      pkg-types: 2.2.0
+      scule: 1.3.0
+      strip-literal: 3.0.0
+      tinyglobby: 0.2.14
+      unplugin: 2.3.5
+      unplugin-utils: 0.2.5
+
+  unist-util-is@6.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+
+  unist-util-stringify-position@4.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+
+  unist-util-visit-parents@6.0.1:
+    dependencies:
+      '@types/unist': 3.0.3
+      unist-util-is: 6.0.0
+
+  unist-util-visit@5.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+      unist-util-is: 6.0.0
+      unist-util-visit-parents: 6.0.1
+
+  universalify@2.0.1: {}
+
+  unocss@66.4.1(postcss@8.5.6)(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)):
+    dependencies:
+      '@unocss/astro': 66.4.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+      '@unocss/cli': 66.4.1
+      '@unocss/core': 66.4.1
+      '@unocss/postcss': 66.4.1(postcss@8.5.6)
+      '@unocss/preset-attributify': 66.4.1
+      '@unocss/preset-icons': 66.4.1
+      '@unocss/preset-mini': 66.4.1
+      '@unocss/preset-tagify': 66.4.1
+      '@unocss/preset-typography': 66.4.1
+      '@unocss/preset-uno': 66.4.1
+      '@unocss/preset-web-fonts': 66.4.1
+      '@unocss/preset-wind': 66.4.1
+      '@unocss/preset-wind3': 66.4.1
+      '@unocss/preset-wind4': 66.4.1
+      '@unocss/transformer-attributify-jsx': 66.4.1
+      '@unocss/transformer-compile-class': 66.4.1
+      '@unocss/transformer-directives': 66.4.1
+      '@unocss/transformer-variant-group': 66.4.1
+      '@unocss/vite': 66.4.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+    optionalDependencies:
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+
+  unplugin-auto-import@20.0.0(@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3))):
+    dependencies:
+      local-pkg: 1.1.1
+      magic-string: 0.30.17
+      picomatch: 4.0.3
+      unimport: 5.2.0
+      unplugin: 2.3.5
+      unplugin-utils: 0.2.5
+    optionalDependencies:
+      '@vueuse/core': 13.6.0(vue@3.5.18(typescript@5.8.3))
+
+  unplugin-combine@2.1.0(esbuild@0.25.8)(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(rollup@4.46.2)(unplugin@2.3.5):
+    optionalDependencies:
+      esbuild: 0.25.8
+      rollup: 4.46.2
+      unplugin: 2.3.5
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+
+  unplugin-utils@0.2.5:
+    dependencies:
+      pathe: 2.0.3
+      picomatch: 4.0.3
+
+  unplugin-vue-components@28.8.0(@babel/parser@7.28.0)(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      chokidar: 3.6.0
+      debug: 4.4.1
+      local-pkg: 1.1.1
+      magic-string: 0.30.17
+      mlly: 1.7.4
+      tinyglobby: 0.2.14
+      unplugin: 2.3.5
+      unplugin-utils: 0.2.5
+      vue: 3.5.18(typescript@5.8.3)
+    optionalDependencies:
+      '@babel/parser': 7.28.0
+    transitivePeerDependencies:
+      - supports-color
+
+  unplugin-vue-define-options@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      ast-walker-scope: 0.8.1
+      unplugin: 2.3.5
+    transitivePeerDependencies:
+      - vue
+
+  unplugin@2.3.5:
+    dependencies:
+      acorn: 8.15.0
+      picomatch: 4.0.3
+      webpack-virtual-modules: 0.6.2
+
+  unstorage@1.16.1:
+    dependencies:
+      anymatch: 3.1.3
+      chokidar: 4.0.3
+      destr: 2.0.5
+      h3: 1.15.4
+      lru-cache: 10.4.3
+      node-fetch-native: 1.6.7
+      ofetch: 1.4.1
+      ufo: 1.6.1
+
+  update-browserslist-db@1.1.3(browserslist@4.25.1):
+    dependencies:
+      browserslist: 4.25.1
+      escalade: 3.2.0
+      picocolors: 1.1.1
+
+  uri-js@4.4.1:
+    dependencies:
+      punycode: 2.3.1
+
+  util-deprecate@1.0.2: {}
+
+  uuid@11.1.0: {}
+
+  varint@6.0.0: {}
+
+  vdirs@0.1.8(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      evtd: 0.2.4
+      vue: 3.5.18(typescript@5.8.3)
+
+  vite-dev-rpc@1.1.0(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)):
+    dependencies:
+      birpc: 2.5.0
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vite-hot-client: 2.1.0(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+
+  vite-hot-client@2.1.0(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)):
+    dependencies:
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+
+  vite-node@3.2.4(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1):
+    dependencies:
+      cac: 6.7.14
+      debug: 4.4.1
+      es-module-lexer: 1.7.0
+      pathe: 2.0.3
+      vite: 7.1.0(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+    transitivePeerDependencies:
+      - '@types/node'
+      - jiti
+      - less
+      - lightningcss
+      - sass
+      - sass-embedded
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+      - tsx
+      - yaml
+
+  vite-plugin-compression@0.5.1(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)):
+    dependencies:
+      chalk: 4.1.2
+      debug: 4.4.1
+      fs-extra: 10.1.0
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  vite-plugin-html@3.2.2(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)):
+    dependencies:
+      '@rollup/pluginutils': 4.2.1
+      colorette: 2.0.20
+      connect-history-api-fallback: 1.6.0
+      consola: 2.15.3
+      dotenv: 16.6.1
+      dotenv-expand: 8.0.3
+      ejs: 3.1.10
+      fast-glob: 3.3.3
+      fs-extra: 10.1.0
+      html-minifier-terser: 6.1.0
+      node-html-parser: 5.4.2
+      pathe: 0.2.0
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+
+  vite-plugin-inspect@11.3.2(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)):
+    dependencies:
+      ansis: 4.1.0
+      debug: 4.4.1
+      error-stack-parser-es: 1.0.5
+      ohash: 2.0.11
+      open: 10.2.0
+      perfect-debounce: 1.0.0
+      sirv: 3.0.1
+      unplugin-utils: 0.2.5
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vite-dev-rpc: 1.1.0(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+    transitivePeerDependencies:
+      - supports-color
+
+  vite-plugin-vue-devtools@7.7.7(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      '@vue/devtools-core': 7.7.7(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(vue@3.5.18(typescript@5.8.3))
+      '@vue/devtools-kit': 7.7.7
+      '@vue/devtools-shared': 7.7.7
+      execa: 9.6.0
+      sirv: 3.0.1
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vite-plugin-inspect: 11.3.2(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+      vite-plugin-vue-inspector: 5.3.2(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+    transitivePeerDependencies:
+      - '@nuxt/kit'
+      - supports-color
+      - vue
+
+  vite-plugin-vue-inspector@5.3.2(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)):
+    dependencies:
+      '@babel/core': 7.28.0
+      '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.0)
+      '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0)
+      '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0)
+      '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.0)
+      '@vue/compiler-dom': 3.5.18
+      kolorist: 1.8.0
+      magic-string: 0.30.17
+      vite: rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  vite@7.1.0(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1):
+    dependencies:
+      esbuild: 0.25.8
+      fdir: 6.4.6(picomatch@4.0.3)
+      picomatch: 4.0.3
+      postcss: 8.5.6
+      rollup: 4.46.2
+      tinyglobby: 0.2.14
+    optionalDependencies:
+      '@types/node': 22.17.0
+      fsevents: 2.3.3
+      jiti: 2.5.1
+      lightningcss: 1.30.1
+      sass: 1.90.0
+      sass-embedded: 1.90.0
+      terser: 5.43.1
+      yaml: 2.8.1
+
+  vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.0)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1):
+    dependencies:
+      '@types/chai': 5.2.2
+      '@vitest/expect': 3.2.4
+      '@vitest/mocker': 3.2.4(vite@7.1.0(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))
+      '@vitest/pretty-format': 3.2.4
+      '@vitest/runner': 3.2.4
+      '@vitest/snapshot': 3.2.4
+      '@vitest/spy': 3.2.4
+      '@vitest/utils': 3.2.4
+      chai: 5.2.1
+      debug: 4.4.1
+      expect-type: 1.2.2
+      magic-string: 0.30.17
+      pathe: 2.0.3
+      picomatch: 4.0.3
+      std-env: 3.9.0
+      tinybench: 2.9.0
+      tinyexec: 0.3.2
+      tinyglobby: 0.2.14
+      tinypool: 1.1.1
+      tinyrainbow: 2.0.0
+      vite: 7.1.0(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      vite-node: 3.2.4(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1)
+      why-is-node-running: 2.3.0
+    optionalDependencies:
+      '@types/debug': 4.1.12
+      '@types/node': 22.17.0
+      '@vitest/ui': 3.2.4(vitest@3.2.4)
+      jsdom: 26.1.0
+    transitivePeerDependencies:
+      - jiti
+      - less
+      - lightningcss
+      - msw
+      - sass
+      - sass-embedded
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+      - tsx
+      - yaml
+
+  vooks@0.2.12(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      evtd: 0.2.4
+      vue: 3.5.18(typescript@5.8.3)
+
+  vscode-jsonrpc@8.2.0: {}
+
+  vscode-languageserver-protocol@3.17.5:
+    dependencies:
+      vscode-jsonrpc: 8.2.0
+      vscode-languageserver-types: 3.17.5
+
+  vscode-languageserver-textdocument@1.0.12: {}
+
+  vscode-languageserver-types@3.17.5: {}
+
+  vscode-languageserver@9.0.1:
+    dependencies:
+      vscode-languageserver-protocol: 3.17.5
+
+  vscode-uri@3.1.0: {}
+
+  vue-component-type-helpers@2.2.12: {}
+
+  vue-draggable-plus@0.6.0(@types/sortablejs@1.15.8):
+    dependencies:
+      '@types/sortablejs': 1.15.8
+
+  vue-eslint-parser@10.2.0(eslint@9.32.0(jiti@2.5.1)):
+    dependencies:
+      debug: 4.4.1
+      eslint: 9.32.0(jiti@2.5.1)
+      eslint-scope: 8.4.0
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      esquery: 1.6.0
+      semver: 7.7.2
+    transitivePeerDependencies:
+      - supports-color
+
+  vue-flow-layout@0.2.0: {}
+
+  vue-macros@3.0.0-beta.21(@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3)))(esbuild@0.25.8)(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(rollup@4.46.2)(typescript@5.8.3)(vue-tsc@3.0.5(typescript@5.8.3))(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      '@vue-macros/better-define': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/boolean-prop': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/chain-call': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/common': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/config': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-emit': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-models': 3.0.0-beta.21(@vueuse/core@13.6.0(vue@3.5.18(typescript@5.8.3)))(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-prop': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-props': 3.0.0-beta.21(@vue-macros/reactivity-transform@3.0.0-beta.21(vue@3.5.18(typescript@5.8.3)))(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-props-refs': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-render': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-slots': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/define-stylex': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/devtools': 3.0.0-beta.21(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(typescript@5.8.3)
+      '@vue-macros/export-expose': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/export-props': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/export-render': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/hoist-static': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/jsx-directive': 3.0.0-beta.21(typescript@5.8.3)
+      '@vue-macros/named-template': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/reactivity-transform': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/script-lang': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/setup-block': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/setup-component': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/setup-sfc': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/short-bind': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/short-emits': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/short-vmodel': 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      '@vue-macros/volar': 3.0.0-beta.21(typescript@5.8.3)(vue-tsc@3.0.5(typescript@5.8.3))(vue@3.5.18(typescript@5.8.3))
+      unplugin: 2.3.5
+      unplugin-combine: 2.1.0(esbuild@0.25.8)(rolldown-vite@7.1.0(@types/node@22.17.0)(esbuild@0.25.8)(jiti@2.5.1)(sass-embedded@1.90.0)(sass@1.90.0)(terser@5.43.1)(yaml@2.8.1))(rollup@4.46.2)(unplugin@2.3.5)
+      unplugin-vue-define-options: 3.0.0-beta.21(vue@3.5.18(typescript@5.8.3))
+      vue: 3.5.18(typescript@5.8.3)
+    transitivePeerDependencies:
+      - '@rspack/core'
+      - '@vueuse/core'
+      - esbuild
+      - rolldown
+      - rollup
+      - typescript
+      - vite
+      - vue-tsc
+      - webpack
+
+  vue-router@4.5.1(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.18(typescript@5.8.3)
+
+  vue-tsc@3.0.5(typescript@5.8.3):
+    dependencies:
+      '@volar/typescript': 2.4.22
+      '@vue/language-core': 3.0.5(typescript@5.8.3)
+      typescript: 5.8.3
+
+  vue-vine-tsc@1.7.0(typescript@5.8.3):
+    dependencies:
+      '@typescript/native-preview': 7.0.0-dev.20250819.1
+      '@volar/language-core': 2.4.22
+      '@volar/typescript': 2.4.22
+      '@vue-vine/language-service': 1.7.0(typescript@5.8.3)
+      '@vue/language-core': 3.0.5(typescript@5.8.3)
+    transitivePeerDependencies:
+      - bufferutil
+      - typescript
+      - utf-8-validate
+
+  vue@3.5.18(typescript@5.8.3):
+    dependencies:
+      '@vue/compiler-dom': 3.5.18
+      '@vue/compiler-sfc': 3.5.18
+      '@vue/runtime-dom': 3.5.18
+      '@vue/server-renderer': 3.5.18(vue@3.5.18(typescript@5.8.3))
+      '@vue/shared': 3.5.18
+    optionalDependencies:
+      typescript: 5.8.3
+
+  vueuc@0.4.64(vue@3.5.18(typescript@5.8.3)):
+    dependencies:
+      '@css-render/vue3-ssr': 0.15.14(vue@3.5.18(typescript@5.8.3))
+      '@juggle/resize-observer': 3.4.0
+      css-render: 0.15.14
+      evtd: 0.2.4
+      seemly: 0.3.10
+      vdirs: 0.1.8(vue@3.5.18(typescript@5.8.3))
+      vooks: 0.2.12(vue@3.5.18(typescript@5.8.3))
+      vue: 3.5.18(typescript@5.8.3)
+
+  w3c-xmlserializer@5.0.0:
+    dependencies:
+      xml-name-validator: 5.0.0
+
+  webidl-conversions@7.0.0: {}
+
+  webpack-virtual-modules@0.6.2: {}
+
+  whatwg-encoding@3.1.1:
+    dependencies:
+      iconv-lite: 0.6.3
+
+  whatwg-mimetype@4.0.0: {}
+
+  whatwg-url@14.2.0:
+    dependencies:
+      tr46: 5.1.1
+      webidl-conversions: 7.0.0
+
+  which@2.0.2:
+    dependencies:
+      isexe: 2.0.0
+
+  which@4.0.0:
+    dependencies:
+      isexe: 3.1.1
+
+  which@5.0.0:
+    dependencies:
+      isexe: 3.1.1
+
+  why-is-node-running@2.3.0:
+    dependencies:
+      siginfo: 2.0.0
+      stackback: 0.0.2
+
+  word-wrap@1.2.5: {}
+
+  wrap-ansi@7.0.0:
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+
+  wrap-ansi@8.1.0:
+    dependencies:
+      ansi-styles: 6.2.1
+      string-width: 5.1.2
+      strip-ansi: 7.1.0
+
+  ws@8.18.3: {}
+
+  wsl-utils@0.1.0:
+    dependencies:
+      is-wsl: 3.1.0
+
+  xml-name-validator@4.0.0: {}
+
+  xml-name-validator@5.0.0: {}
+
+  xmlchars@2.2.0: {}
+
+  yallist@3.1.1: {}
+
+  yaml-eslint-parser@1.3.0:
+    dependencies:
+      eslint-visitor-keys: 3.4.3
+      yaml: 2.8.1
+
+  yaml@2.8.1: {}
+
+  yocto-queue@0.1.0: {}
+
+  yocto-queue@1.2.1: {}
+
+  yoctocolors@2.1.1: {}
+
+  zwitch@2.0.4: {}

+ 3 - 0
pnpm-workspace.yaml

@@ -0,0 +1,3 @@
+onlyBuiltDependencies:
+  - core-js
+  - esbuild

BIN
public/android-chrome-192x192.png


BIN
public/android-chrome-512x512.png


BIN
public/apple-touch-icon.png


+ 1 - 0
public/assets/preloader.css

@@ -0,0 +1 @@
+#appLoader{--color:#fafafa;--clock-color:#28364c;.dark &{--clock-color:#bcbcbc;--color:#171717;}position:fixed;inset:0;height:100dvh;background-color:var(--color);display:grid;place-items:center;}#clock{width:30px;height:30px;border:1px solid;border-radius:50%;color:var(--clock-color);transform:scale(2.5);&::before,&::after{position:absolute;width:0;border-left:1px solid var(--clock-color);content:'';border-radius:1px;transform-origin:bottom center;animation:clockwise linear infinite;left:50%;margin-left:-0.5px;}&::before{height:12px;top:calc(50% - 12px);animation-duration:1s;}&::after{height:10px;top:calc(50% - 10px);animation-duration:60s;}}@keyframes clockwise{to{transform:rotate(360deg);}}

BIN
public/favicon-16x16.png


BIN
public/favicon-32x32.png


BIN
public/favicon-vue.ico


BIN
public/favicon.ico


+ 11 - 0
public/site.webmanifest

@@ -0,0 +1,11 @@
+{
+  "name": "",
+  "short_name": "",
+  "icons": [
+    { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
+    { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
+  ],
+  "theme_color": "#ffffff",
+  "background_color": "#ffffff",
+  "display": "standalone"
+}

+ 6 - 0
service/.env

@@ -0,0 +1,6 @@
+APP_ENV="dev"
+APP_PORT="8301"
+MYSQL_DSN="admin:V1hdkLzRoleI0YG6If24@tcp(34.211.183.235:3308)/home_energy_storage_server?charset=utf8mb4&parseTime=True&loc=Local"
+POSTGRES_DSN="host=localhost user=fwh_toolbox password=fwh_toolbox dbname=fwh_toolbox_db port=5432 sslmode=disable"
+MONGO_DSN="mongodb://127.0.0.1:27017/FwhToolboxDbDev"
+REDIS_ADDR="localhost"

+ 0 - 0
service/.vscode/settings.json


+ 69 - 0
service/README.md

@@ -0,0 +1,69 @@
+# FranklinWH Toolbox Service
+
+Go 后端服务,整合了:
+
+- Fiber(Web 框架)
+- GORM(ORM)
+- Swagger(接口文档)
+- 缓存(Redis)
+- 任务 + 定时器(Cron)
+- 日志(Zap 或 Logrus)
+- 配置管理(Viper)
+
+## 📁 项目结构
+
+```bash
+├── cmd/
+│    └── main.go
+├── config/             # 配置
+│    └── config.go
+├── cache/              # 缓存封装(Redis)
+├── database/
+│    └── database.go    # GORM 初始化
+├── logger/
+│    └── logger.go
+├── scheduler/          # 任务调度
+│    └── scheduler.go
+├── routes/
+│    └── routes.go      # Fiber 路由 & swagger
+├── controllers/        # 控制器
+├── models/             # GORM 模型
+├── services/           # 业务逻辑
+├── middleware/         # 中间件
+├── pkg/                # 自定义通用包
+├── docs/               # Swagger 文档
+├── .env                # 环境变量
+├── go.mod
+└── go.sum
+```
+
+## ✅ 项目特点 & 核心依赖
+
+- 配置: Viper(加载 .env、支持环境变量)
+- ORM: GORM — 数据库操作 + AutoMigrate 自动建表
+- 缓存: Redis(Go‑redis 客户端)
+- 定时任务: robfig/cron
+- 日志: Zap(结构化日志)
+- 文档: swaggo + swagger UI 自动生成 Swagger 文档
+- 任务调度: Cron 支持定时和任务计划
+
+## 📌 源模板参考
+
+- alpody/golang-fiber-realworld-example-app 项目提供了 Fiber + GORM + Swagger 等完整的实战案例
+- Fiber 官方 recipes/gorm-example 示例展示了 GORM 和 Fiber 的集成方式
+
+## ⚙️ 配置说明
+
+项目可通过环境变量配置文件对软件的功能进行配置,配置文件支持的路径如下(读取优先级与路径列出顺序一致):
+
+- "./.env"
+- "./config.env"
+
+| 变量名 | 说明 |
+| - | --- |
+| APP_ENV | 运行环境,可选项:"pro" - 生产环境、"dev" - 开发环境,默认"DEV" |
+| APP_PORT | 服务监听端口号,默认"8301" |
+| MYSQL_DSN | MYSQL数据库连接配置,DSN格式,连接FWH服务器的home_energy_storage_server数据库,用于查询站点信息。 |
+| POSTGRES_DSN | PGSQL数据库连接配置,DSN格式,本地保存数据信息。 |
+| MONGO_DSN | MONGO数据库连接配置,DSN格式,本地保存数据信息。 |
+| REDIS_ADDR | REDIS本地缓存服务器的域名或IP地址,默认"localhost" |

+ 30 - 0
service/cache/cache.go

@@ -0,0 +1,30 @@
+package cache
+
+import (
+	"context"
+	"time"
+
+	"github.com/go-redis/redis/v8"
+)
+
+var C *redis.Client
+
+func Init(addr string) {
+	C = redis.NewClient(&redis.Options{
+		Addr: addr,
+		DB:   12,
+	})
+}
+
+func GetOrSet(ctx context.Context, key string, ttl time.Duration, fetch func() (interface{}, error)) (interface{}, error) {
+	val, err := C.Get(ctx, key).Result()
+	if err == nil {
+		return val, nil
+	}
+	result, err := fetch()
+	if err != nil {
+		return nil, err
+	}
+	C.Set(ctx, key, result, ttl)
+	return result, nil
+}

+ 52 - 0
service/cmd/deploy.sh

@@ -0,0 +1,52 @@
+#!/bin/bash
+set -e
+
+# ===== 配置部分 =====
+APP_NAME="fwh-toolbox-server"                       # 服务名称
+APP_DIR="/var/www/fwh-toolbox/server"               # 二进制运行路径
+APP_PATH="$APP_DIR/$APP_NAME"                       # 二进制安装路径
+SERVICE_FILE="/etc/systemd/system/$APP_NAME.service"
+
+# ===== 编译程序 =====
+echo "🚀 Building Go program..."
+GOOS=linux GOARCH=amd64 go build -o $APP_NAME ./   # 按需修改 ./ 为你的项目路径
+
+# ===== 复制程序到目标路径 =====
+echo "📦 Copying binary to $APP_PATH..."
+sudo mv $APP_NAME $APP_PATH
+sudo chmod +x $APP_PATH
+
+# ===== 安装 systemd 服务(如果不存在) =====
+if [ ! -f "$SERVICE_FILE" ]; then
+    echo "📝 Creating systemd service: $SERVICE_FILE"
+    sudo tee $SERVICE_FILE > /dev/null <<EOF
+[Unit]
+Description=$APP_NAME service
+After=network.target
+
+[Service]
+ExecStart=$APP_PATH
+Restart=always
+User=root
+WorkingDirectory=$APP_DIR
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+    echo "✅ Service file created."
+    sudo systemctl daemon-reload
+    sudo systemctl enable $APP_NAME
+fi
+
+# ===== 启动或重启服务 =====
+if systemctl is-active --quiet $APP_NAME; then
+    echo "🔄 Restarting $APP_NAME service..."
+    sudo systemctl restart $APP_NAME
+else
+    echo "▶️ Starting $APP_NAME service..."
+    sudo systemctl start $APP_NAME
+fi
+
+# ===== 状态检查 =====
+sudo systemctl status $APP_NAME --no-pager

+ 69 - 0
service/cmd/gen/fwh_pro.go

@@ -0,0 +1,69 @@
+package main
+
+import (
+	"fmt"
+	"fwh-toolbox-server/config"
+	"fwh-toolbox-server/database"
+
+	"gorm.io/gen"
+)
+
+// Dynamic SQL
+type Querier interface {
+	// SELECT * FROM @@table WHERE name = @name{{if role !=""}} AND role = @role{{end}}
+	FilterWithNameAndRole(name, role string) ([]gen.T, error)
+}
+
+func main() {
+	// 指定生成代码的具体相对目录(相对于当前文件),默认:./query
+	// 默认生成需要使用WithContext之后才可以查询的代码,但可以通过设置gen.WithoutContext禁用该模式
+	g := gen.NewGenerator(gen.Config{
+		// 默认会在 OutPath 目录生成CRUD代码,并且同目录下生成 model 包
+		// 所以OutPath最终package不能设置为model,在有数据库表同步的情况下会产生冲突
+		// 若一定要使用,可以通过ModelPkgPath单独指定model package的名称
+		OutPath: "./models/fwh_pro/query",
+		// ModelPkgPath: "./model",
+
+		// gen.WithoutContext: 禁用WithContext模式
+		// gen.WithDefaultQuery: 生成一个全局Query对象Q
+		// gen.WithQueryInterface: 生成Query接口
+		Mode: gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
+	})
+
+	// 通常复用项目中已有的SQL连接配置db(*grom.DB)
+	// 非必须,但如果需要复用连接是的gorm.Config或需要连接数据库同步表信息则必须设置
+	// gormdb, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
+	// g.UseDB(gormdb) // reuse your gorm db
+	cfg, _ := config.LoadConfig()
+	db, err := database.InitMySQL(cfg.MySQLDSN)
+	if err != nil {
+		fmt.Printf("Database Open Filed!")
+		return
+	}
+
+	g.UseDB(db)
+
+	// 从连接的数据库为所有表生成Model结构体和CRUD代码
+	// g.ApplyBasic(g.GenerateAllTable()...)
+	// 也可以手动指定需要生成代码的数据表
+	g.ApplyBasic(g.GenerateModel("iot_gateway", gen.FieldIgnore(
+		"power_sun",      // 太阳能发电功率
+		"power_gen",      // 发电机发电功率
+		"power_uti",      // 市电供电功率
+		"power_fhp",      // FHP 发电功率
+		"power_load",     // 负荷功率
+		"battery_soc",    // 电池 SOC
+		"weather_county", // weather 天气 对应的县ID(zoneId)
+		"weather_state",  // weather 天气 对应的州(area)
+		"gen_stat",       // 发电机状态(0不使能 1停机 2启动 3运行 4退出 5故障)
+		"main_sw",        // 主开关状态(1,0,1 备电主开关 发电机主开关 光伏主开关 0-断开 1-闭合 数据来源201)
+	), gen.FieldGenType("ger_option", "Int8")))
+	// Generate basic type-safe DAO API for struct `model.User` following conventions
+	// g.ApplyBasic(model.User{})
+
+	// Generate Type Safe API with Dynamic SQL defined on Querier interface for `model.User` and `model.Company`
+	// g.ApplyInterface(func(Querier) {}, model.User{}, model.Company{})
+
+	// Generate the code
+	g.Execute()
+}

+ 65 - 0
service/config/config.go

@@ -0,0 +1,65 @@
+package config
+
+import (
+	"fmt"
+
+	"github.com/spf13/viper"
+)
+
+type Config struct {
+	AppEnv      string
+	AppPort     string
+	RedisAddr   string
+	MySQLDSN    string
+	PostgresDSN string
+	MongoDSN    string
+}
+
+// 需先执行LoadConfig后才能读取APP_ENV
+var APP_ENV string
+
+// LoadConfig 读取配置文件和环境变量
+func LoadConfig() (*Config, error) {
+	v := viper.New()
+
+	// 默认值(避免缺省时崩溃)
+	v.SetDefault("APP_ENV", "dev")
+	v.SetDefault("APP_PORT", "8080")
+	v.SetDefault("REDIS_ADDR", "localhost:6379")
+	v.SetDefault("MYSQL_DSN", "")
+	v.SetDefault("POSTGRES_DSN", "host=localhost user=fwh_toolbox password=fwh_toolbox dbname=fwh_toolbox_db port=5432 sslmode=disable")
+	v.SetDefault("MONGO_DSN", "mongodb://localhost:27017/FwhToolboxDbDev")
+
+	// 多文件路径支持
+	paths := []string{"./.env", "./config.env"}
+	var readErr error
+	for _, path := range paths {
+		v.SetConfigFile(path)
+		if err := v.ReadInConfig(); err == nil {
+			fmt.Printf("✅ Using config file: %s\n", path)
+			readErr = nil
+			break
+		} else {
+			readErr = err
+		}
+	}
+
+	// 环境变量覆盖文件配置
+	v.AutomaticEnv()
+
+	// 如果两个文件都没读到,也没关系,直接用默认值
+	if readErr != nil {
+		fmt.Println("⚠️ No config file found, using default values & environment variables")
+	}
+
+	APP_ENV = v.GetString("APP_ENV")
+
+	return &Config{
+		AppEnv:      v.GetString("APP_ENV"),
+		AppPort:     v.GetString("APP_PORT"),
+		RedisAddr:   v.GetString("REDIS_ADDR"),
+		MySQLDSN:    v.GetString("MYSQL_DSN"),
+		PostgresDSN: v.GetString("POSTGRES_DSN"),
+		MongoDSN:    v.GetString("MONGO_DSN"),
+	}, nil
+}

+ 3 - 0
service/controllers/controllers.go

@@ -0,0 +1,3 @@
+package controllers
+
+

+ 96 - 0
service/controllers/mysql_controllers.go

@@ -0,0 +1,96 @@
+package controllers
+
+import (
+	"context"
+	"fmt"
+	"fwh-toolbox-server/models/fwh_pro/query"
+	"log"
+	"time"
+
+	"gorm.io/gen"
+	"gorm.io/gen/field"
+)
+
+type SiteListFilter struct {
+	ID              []string
+	Version         []string
+	ActiveStatus    []int32
+	ActiveTime      []time.Time // 仅有两个值时有效
+	LocalActiveTime []time.Time // 仅有两个值时有效
+	FhpSn           []string
+	Country         []string
+	Province        []string
+	City            []string
+}
+
+func GetSiteList(ctx context.Context, filter SiteListFilter, offset int, limit int) {
+	Q := query.IotGateway
+	q := Q.WithContext(ctx)
+
+	if offset >= 0 {
+		q = q.Offset(limit)
+	}
+
+	if limit >= 0 {
+		q = q.Limit(limit)
+	}
+
+	// 必须显示项,可动态添加
+	selectList := []field.Expr{Q.ID, Q.Version, Q.FhpSn, Q.ElecnetStatus, Q.Country, Q.Province, Q.City, Q.FinancierName}
+
+	// 查询指定字段
+	q = q.Select(selectList...)
+
+	var condition []gen.Condition
+
+	if len(filter.ID) > 0 {
+		condition = append(condition, Q.ID.In(filter.ID...))
+	}
+
+	if len(filter.Version) > 0 {
+		condition = append(condition, Q.Version.In(filter.Version...))
+	}
+
+	if len(filter.ActiveStatus) > 0 {
+		condition = append(condition, Q.ActiveStatus.In(filter.ActiveStatus...))
+	}
+
+	if len(filter.ActiveTime) == 2 {
+		condition = append(condition, Q.ActiveTime.Between(filter.ActiveTime[0], filter.ActiveTime[1]))
+	}
+
+	if len(filter.LocalActiveTime) == 2 {
+		condition = append(condition, Q.LocalActiveTime.Between(filter.LocalActiveTime[0], filter.LocalActiveTime[1]))
+	}
+
+	// 查找aPower SN
+	for i := 0; i < len(filter.FhpSn); i++ {
+		condition = append(condition, Q.FhpSn.Like("%"+filter.FhpSn[i]+"%"))
+	}
+
+	if len(filter.Country) > 0 {
+		condition = append(condition, Q.Country.In(filter.Country...))
+	}
+
+	if len(filter.Province) > 0 {
+		condition = append(condition, Q.Province.In(filter.Province...))
+	}
+
+	if len(filter.City) > 0 {
+		condition = append(condition, Q.City.In(filter.City...))
+	}
+
+	// 过滤条件
+	siteList, err := q.Where(condition...).Find()
+	if err != nil {
+		log.Fatal("Failed to query: ", err)
+		return
+	}
+
+	// 打印查询到的数据
+	for _, site := range siteList {
+		fmt.Printf("ID: %s, Name: %s\n", site.ID, site.Name)
+	}
+
+	return
+}

+ 356 - 0
service/controllers/sitesMonitorControllers.go

@@ -0,0 +1,356 @@
+package controllers
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fwh-toolbox-server/database"
+	"fwh-toolbox-server/ext/api/fwh_pro"
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+type StateConfig struct {
+	Enable bool `bson:"enable" json:"enable"`
+}
+
+type SocConfig struct {
+	Enable bool `bson:"enable" json:"enable"`
+}
+
+type APowerConfig struct {
+	Enable bool `bson:"enable" json:"enable"`
+}
+
+type AlarmConfig struct {
+	Enable bool `bson:"enable" json:"enable"`
+}
+
+type VersionConfig struct {
+	Enable bool `bson:"enable" json:"enable"`
+}
+
+type TariffsConfig struct {
+	Enable bool `bson:"enable" json:"enable"`
+}
+
+type OthersConfig struct {
+	Enable bool `bson:"enable" json:"enable"`
+}
+
+type MonitorPageConfig struct {
+	State   StateConfig   `bson:"state" json:"state"`
+	Soc     SocConfig     `bson:"soc" json:"soc"`
+	APower  APowerConfig  `bson:"aPower" json:"aPower"`
+	Alarm   AlarmConfig   `bson:"alarm" json:"alarm"`
+	Version VersionConfig `bson:"version" json:"version"`
+	Tariffs TariffsConfig `bson:"tariffs" json:"tariffs"`
+	Others  OthersConfig  `bson:"others" json:"others"`
+}
+
+type SitesMonitorConfig struct {
+	Name        string             `bson:"name" json:"name"`
+	Description string             `bson:"description" json:"description"`
+	SiteList    []string           `bson:"siteList" json:"siteList"`
+	PageConfig  *MonitorPageConfig `bson:"pageConfig,omitempty" json:"pageConfig,omitempty"`
+}
+
+type SiteInfoPara struct {
+	SiteList []string `json:"siteList"`
+	PageSize uint     `json:"pageSize"`
+	PageNum  uint     `json:"pageNum"`
+}
+
+type SiteInfo struct {
+	Id               string  `json:"id"`
+	Name             *string `json:"name"`
+	FhpSn            string  `json:"fhpSn"`
+	ActiveStatus     int     `json:"activeStatus"`
+	ActiveTime       string  `json:"activeTime"`
+	Country          string  `json:"country"`
+	Province         string  `json:"province"`
+	City             string  `json:"city"`
+	OnOffStatus      int     `json:"onOffStatus"`
+	RealSysHdVersion string  `json:"realSysHdVersion"`
+	RunStatus        int     `json:"runStatus"`
+	Version          string  `json:"version"`
+}
+
+type SiteInfoResponse struct {
+	Code   int        `json:"code"`
+	Msg    string     `json:"msg"`
+	Result []SiteInfo `json:"result"`
+}
+
+type WorkModeInfo struct {
+	Id                   uint     `json:"id"`
+	OldIndex             uint     `json:"oldIndex"`
+	Name                 string   `json:"name"`
+	Soc                  float32  `json:"soc"`
+	MaxSoc               float32  `json:"maxSoc"`
+	MinSoc               float32  `json:"minSoc"`
+	DischargeDepthSoc    *float32 `json:"dischargeDepthSoc"`
+	WorkMode             uint     `json:"workMode"`
+	EnergyIncentivesType uint     `json:"energyIncentivesType"`
+	ElectricityType      uint     `json:"electricityType"`
+}
+
+type SiteModeInfo struct {
+	CurrendId uint           `json:"currendId"`
+	List      []WorkModeInfo `json:"list"`
+}
+
+type SiteModeInfoResponse struct {
+	Code    int          `json:"code"`
+	Msg     string       `json:"msg"`
+	Result  SiteModeInfo `json:"result"`
+	Success bool         `json:"success"`
+}
+
+type SitePowerFlow struct {
+	Index           uint      `json:"index"`           // 非当天为0,当天为实时数量
+	DeviceTimeArray []string  `json:"deviceTimeArray"` // 设备本地时间,格式 “2025-08-18 00:00:00”
+	PowerFhpArray   []float32 `json:"powerFhpArray"`
+	PowerGenArray   []float32 `json:"powerGenArray"`
+	PowerLoadArray  []float32 `json:"powerLoadArray"`
+	PowerSunArray   []float32 `json:"powerSunArray"`
+	PowerUtiArray   []float32 `json:"powerUtiArray"`
+	SocArray        []float32 `json:"socArray"`
+	RunStatusArray  []uint    `json:"runStatusArray"` // 运行状态,0:待机;1:充电;2:放电;3:故障
+	KwhTotalArray   []float32 `json:"kwhTotalArray"`  // 总可用电量,应该是根据SOC推算出来的。
+}
+
+type SitePowerFlowResponse struct {
+	Code   int           `json:"code"`
+	Msg    string        `json:"msg"`
+	Result SitePowerFlow `json:"result"`
+}
+
+// 从数据库获取站点监控配置列表
+func GetSiteMonitorConfig() ([]SitesMonitorConfig, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	collection := database.GetCollection("SiteMonitorConfig")
+	cursor, err := collection.Find(ctx, bson.M{}) // 空条件,查全部
+	if err != nil {
+		return nil, err
+	}
+	defer cursor.Close(ctx)
+
+	var configList []SitesMonitorConfig
+	for cursor.Next(ctx) {
+		var cfg SitesMonitorConfig
+		if err := cursor.Decode(&cfg); err != nil {
+			return nil, err
+		}
+		configList = append(configList, cfg)
+	}
+
+	return configList, nil
+}
+
+// 从数据库获取监控名列表
+func GetSiteMonitorNameList() ([]string, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	collection := database.GetCollection("SiteMonitorConfig")
+	cursor, err := collection.Find(
+		ctx,
+		bson.M{}, // 空条件,查全部
+		options.Find().SetProjection(bson.M{
+			"name": 1, // 只要 name 字段
+			"_id":  0, // 不要 _id 字段(默认会返回)
+		}),
+	)
+	if err != nil {
+		return nil, err
+	}
+	defer cursor.Close(ctx)
+
+	var nameList []string
+	for cursor.Next(ctx) {
+		var result struct {
+			Name string `bson:"name"`
+		}
+		if err := cursor.Decode(&result); err != nil {
+			return nil, err
+		}
+		nameList = append(nameList, result.Name)
+	}
+
+	return nameList, nil
+}
+
+// 从数据库获取一条站点监控配置
+func GetOneSiteMonitorConfig(name string) ([]SitesMonitorConfig, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	collection := database.GetCollection("SiteMonitorConfig")
+	var cfg SitesMonitorConfig
+	err := collection.FindOne(ctx, bson.M{"name": name}).Decode(&cfg)
+	if err == mongo.ErrNoDocuments {
+		return []SitesMonitorConfig{}, nil
+	} else if err != nil {
+		return nil, err
+	}
+
+	var configList []SitesMonitorConfig
+	configList = append(configList, cfg)
+
+	return configList, nil
+}
+
+// 插入一条站点监控配置到数据库
+func InsertSiteMonitorConfig(config SitesMonitorConfig) error {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	collection := database.GetCollection("SiteMonitorConfig")
+
+	// _, err := collection.InsertOne(ctx, config)
+	filter := bson.M{"name": config.Name} // 按用户名匹配
+	update := bson.M{
+		"$set": config, // 如果存在就更新所有字段
+	}
+
+	opts := options.Update().SetUpsert(true)
+
+	_, err := collection.UpdateOne(ctx, filter, update, opts)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// 更新一条站点监控配置到数据库
+func UpdateSiteMonitorConfig(name string, config SitesMonitorConfig) error {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	collection := database.GetCollection("SiteMonitorConfig")
+
+	// _, err := collection.InsertOne(ctx, config)
+	filter := bson.M{"name": name} // 按用户名匹配
+	update := bson.M{
+		"$set": config, // 如果存在就更新所有字段
+	}
+
+	opts := options.Update().SetUpsert(true)
+
+	_, err := collection.UpdateOne(ctx, filter, update, opts)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// 删除一条站点监控配置
+func DeleteSiteMonitorConfig(name string) error {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	filter := bson.M{"name": name} // 删除条件
+	collection := database.GetCollection("SiteMonitorConfig")
+	_, err := collection.DeleteOne(ctx, filter)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// 更新一条站点监控页面配置到数据库
+func UpdateSiteMonitorPageConfig(name string, config MonitorPageConfig) error {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	collection := database.GetCollection("SiteMonitorConfig")
+
+	// _, err := collection.InsertOne(ctx, config)
+	filter := bson.M{"name": name} // 按用户名匹配
+	update := bson.M{
+		"$set": bson.M{
+			"pageConfig": config, // 更新页面配置字段
+		},
+	}
+
+	opts := options.Update().SetUpsert(true)
+
+	_, err := collection.UpdateOne(ctx, filter, update, opts)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// 从fwh_pro获取站点信息表
+func QuerySiteInfoList(para SiteInfoPara) (SiteInfoResponse, error) {
+	resByte, err := fwh_pro.QuerySiteInfoList(para.SiteList, para.PageSize, para.PageNum)
+	if err != nil {
+		return SiteInfoResponse{}, err
+	}
+	var siteInfoRes SiteInfoResponse
+	// 反序列化(unmarshal)
+	jsonErr := json.Unmarshal(resByte, &siteInfoRes)
+	if jsonErr != nil {
+		return SiteInfoResponse{}, jsonErr
+	}
+
+	if siteInfoRes.Code == 401 {
+		// TODO 尝试重新登录
+		return SiteInfoResponse{}, errors.New("certification expired")
+	}
+
+	return siteInfoRes, nil
+}
+
+// 从fwh_pro获取站点工作模式信息
+func QuerySiteModeInfo(site string) (SiteModeInfoResponse, error) {
+	resByte, err := fwh_pro.QuerySiteModeInfo(site)
+	if err != nil {
+		return SiteModeInfoResponse{}, err
+	}
+	var siteModeInfoRes SiteModeInfoResponse
+	// 反序列化(unmarshal)
+	jsonErr := json.Unmarshal(resByte, &siteModeInfoRes)
+	if jsonErr != nil {
+		return SiteModeInfoResponse{}, jsonErr
+	}
+
+	if siteModeInfoRes.Code == 401 {
+		// TODO 尝试重新登录
+		return SiteModeInfoResponse{}, errors.New("certification expired")
+	}
+
+	return siteModeInfoRes, nil
+}
+
+// 从fwh_pro获取站点功率流
+func QuerySitePowerFlow(site string, day time.Time) (SitePowerFlowResponse, error) {
+	resByte, err := fwh_pro.QuerySitePowerFlow(site, day)
+	if err != nil {
+		return SitePowerFlowResponse{}, err
+	}
+	var sitePowerFlowRes SitePowerFlowResponse
+	// 反序列化(unmarshal)
+	jsonErr := json.Unmarshal(resByte, &sitePowerFlowRes)
+	if jsonErr != nil {
+		return SitePowerFlowResponse{}, jsonErr
+	}
+
+	if sitePowerFlowRes.Code == 401 {
+		// TODO 尝试重新登录
+		return SitePowerFlowResponse{}, errors.New("certification expired")
+	}
+
+	return sitePowerFlowRes, nil
+}

+ 18 - 0
service/database/database.go

@@ -0,0 +1,18 @@
+package database
+
+import (
+	"fwh-toolbox-server/config"
+)
+
+func Init(cfg *config.Config) error {
+	// if _, err := InitMySQL(cfg.MySQLDSN); err != nil {
+	// 	return err
+	// }
+	if _, err := InitPostgreSQL(cfg.PostgresDSN); err != nil {
+		return err
+	}
+	if _, err := InitMongo(cfg.MongoDSN); err != nil {
+		return err
+	}
+	return nil
+}

+ 43 - 0
service/database/mongo.go

@@ -0,0 +1,43 @@
+package database
+
+import (
+	"context"
+	"log"
+	"strings"
+	"time"
+
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+var MongoClient *mongo.Client
+var MongoDb *mongo.Database
+
+func InitMongo(uri string) (*mongo.Client, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	defer cancel()
+
+	client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
+	if err != nil {
+		log.Fatal("Mongo connect error:", err)
+		return nil, err
+	}
+
+	if err := client.Ping(ctx, nil); err != nil {
+		log.Fatal("Mongo ping error:", err)
+		return nil, err
+	}
+
+	MongoClient = client
+	log.Println("MongoDB connected:", uri)
+
+	parts := strings.Split(uri, "/")
+	dbName := parts[len(parts)-1] // 最后一段就是数据库名
+	MongoDb = client.Database(dbName)
+
+	return client, nil
+}
+
+func GetCollection(collName string) *mongo.Collection {
+	return MongoDb.Collection(collName)
+}

+ 32 - 0
service/database/mysql.go

@@ -0,0 +1,32 @@
+package database
+
+import (
+	"log"
+
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
+
+	fwh_pro_query "fwh-toolbox-server/models/fwh_pro/query"
+)
+
+var MySQL *gorm.DB
+
+func InitMySQL(dsn string) (*gorm.DB, error) {
+	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
+		Logger: logger.Default.LogMode(logger.Info),
+	})
+	if err != nil {
+		log.Printf("failed to connect to MySQL: %v", err)
+		return nil, err
+	}
+	MySQL = db
+
+	// 初始化Query
+	fwh_pro_query.SetDefault(db)
+
+	return db, nil
+}
+
+// 示例DSN格式:
+// user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local&readTimeout=30s

+ 25 - 0
service/database/pgsql.go

@@ -0,0 +1,25 @@
+package database
+
+import (
+	"log"
+
+	"gorm.io/driver/postgres"
+	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
+)
+
+var PgSQL *gorm.DB
+
+func InitPostgreSQL(dsn string) (*gorm.DB, error) {
+	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
+		Logger: logger.Default.LogMode(logger.Info),
+	})
+	if err != nil {
+		log.Printf("failed to connect to PostgreSQL: %v", err)
+		return nil, err
+	}
+	PgSQL = db
+	// 自动迁移
+	// db.AutoMigrate(&models.User{}, …)
+	return db, nil
+}

+ 73 - 0
service/database/sql.md

@@ -0,0 +1,73 @@
+# SQL 语法
+
+[SQL](./sql.png)
+
+## 基本查询语法
+
+### SELECT
+
+### WHERE
+
+### ORDER BY
+
+### DISTINCT
+
+### LIMIT
+
+## 表操作语句
+
+### CREATE TABLE
+
+### ALTER TABLE
+
+### DROP TABLE
+
+## 子查询与联接
+
+### INNER JOIN
+
+### LEFT JOIN
+
+### RIGHT JOIN
+
+### FULL JOIN
+
+### SUBQUERY
+
+## 函数与聚合操作
+
+### COUNT
+
+### SUM
+
+### AVG
+
+### MIN
+
+### MAX
+
+## 数据操作语句
+
+### INSERT INTO
+
+### UPDATE
+
+### DELETE
+
+## 高级操作
+
+### UNION
+
+### CASE
+
+### INDEX
+
+## 性能优化与安全性
+
+### EXPLAIN
+
+### TRANSACTION
+
+### GRANT
+
+### REVOKE

BIN
service/database/sql.png


+ 147 - 0
service/database/sql/home_energy_storage_server_iot_gateway_2025-08-11_152722.sql

@@ -0,0 +1,147 @@
+-- MySQL dump 10.13  Distrib 8.0.42, for Linux (x86_64)
+--
+-- Host: 34.211.183.235    Database: home_energy_storage_server
+-- ------------------------------------------------------
+-- Server version	8.0.32
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!50503 SET NAMES utf8mb4 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
+SET @@SESSION.SQL_LOG_BIN= 0;
+
+--
+-- GTID state at the beginning of the backup 
+--
+
+SET @@GLOBAL.GTID_PURGED=/*!80000 '+'*/ '';
+
+--
+-- Table structure for table `iot_gateway`
+--
+
+DROP TABLE IF EXISTS `iot_gateway`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `iot_gateway` (
+  `id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT '主键ID(网关序列号)',
+  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '网关名称(站点、可自定义)',
+  `version` varchar(64) DEFAULT NULL COMMENT '版本',
+  `distributor_id` int DEFAULT NULL COMMENT '分销商公司ID',
+  `installer_id` int DEFAULT NULL COMMENT '安装商公司ID',
+  `maint_id` int DEFAULT NULL COMMENT '维护商公司ID',
+  `account` varchar(64) DEFAULT NULL COMMENT '绑定用户账号(邮箱)',
+  `longitude` varchar(100) DEFAULT NULL COMMENT '安装地理信息(经度)',
+  `latitude` varchar(100) DEFAULT NULL COMMENT '安装地理信息(纬度)',
+  `gps_address` varchar(255) DEFAULT NULL COMMENT '通过谷歌地图api解析经纬度出来的地址',
+  `address` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '安装地理信息(详细地址)(app生成并允许用户输入的详细地址)',
+  `active_status` int DEFAULT '0' COMMENT '激活状态(0未激活 1已激活)',
+  `active_time` datetime DEFAULT NULL COMMENT '激活时间',
+  `local_active_time` datetime DEFAULT NULL COMMENT '设备当地激活时间',
+  `status` int(1) unsigned zerofill DEFAULT '0' COMMENT '网关状态(0离线 1正常 )',
+  `on_line_time` datetime DEFAULT NULL COMMENT '在线时间',
+  `off_line_time` varchar(64) DEFAULT NULL COMMENT '离线时间',
+  `model` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '型号',
+  `device_ip` varchar(100) DEFAULT NULL COMMENT '设备IP地址',
+  `subnet` varchar(100) DEFAULT NULL COMMENT '子网掩码',
+  `mac` varchar(100) DEFAULT NULL COMMENT 'MAC',
+  `gateway_ip` varchar(100) DEFAULT NULL COMMENT '网关IP地址',
+  `signal` varchar(100) DEFAULT NULL COMMENT '信号强度',
+  `icc_id` varchar(100) DEFAULT NULL COMMENT 'ICCID',
+  `imei` varchar(100) DEFAULT NULL COMMENT 'IMEI',
+  `firm_upgrade_time` datetime DEFAULT NULL COMMENT '最后固件升级时间',
+  `control_json` json DEFAULT NULL COMMENT '控制JSON',
+  `turnover_time` datetime DEFAULT NULL COMMENT '更新时间',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `power_sun` decimal(20,2) DEFAULT NULL COMMENT '太阳能发电功率',
+  `power_gen` decimal(20,2) DEFAULT NULL COMMENT '发电机发电功率',
+  `power_uti` decimal(20,2) DEFAULT NULL COMMENT '市电供电功率',
+  `power_fhp` decimal(20,2) DEFAULT NULL COMMENT 'FHP 发电功率',
+  `power_load` decimal(20,2) DEFAULT NULL COMMENT '负荷功率',
+  `battery_soc` decimal(20,2) DEFAULT NULL COMMENT '电池 SOC ',
+  `run_status` int DEFAULT '0' COMMENT 'EMS 当前运行状态 0:待机;1:充电;2:放电;3:故障',
+  `ems_model` int DEFAULT NULL COMMENT 'EMS 当前工作模式 1:自发自用;2:TOU(经济);3:TOU(平衡);4:等待备电;5:等待风暴;6:离网电池备电;7:离网发电机备电;8:VPP 调度;9:紧急停止;10:维护停止;11:复位自检;89:调试模式;99:故障停止。',
+  `electricity_type` int DEFAULT NULL COMMENT '电费类型.1:分时电价;2:阶梯电价;3:固定电价;4:分时&阶梯,5:BB 套餐 6:peak demand',
+  `fhp_sn` varchar(3000) DEFAULT NULL COMMENT 'FHP 设备编号 INFI设备数组',
+  `sort` int NOT NULL AUTO_INCREMENT COMMENT '自增长序列排序',
+  `device_control_data` json DEFAULT NULL COMMENT 'IOT-设备控制设置',
+  `tou_param_setting_data` json DEFAULT NULL COMMENT 'IOT-TOU参数配置',
+  `time_zone` float(3,1) DEFAULT '8.0' COMMENT '时区',
+  `zone_info` varchar(40) DEFAULT NULL COMMENT '地区代表时区。例如:America/New_York',
+  `dst` int DEFAULT NULL COMMENT '0:协调世界时间-N,1:自动调整夏令时',
+  `ibg_main_version` varchar(64) DEFAULT NULL COMMENT 'nsc支持,main_ver版本信息',
+  `aws_version` varchar(64) DEFAULT NULL COMMENT '云平台版本信息',
+  `app_version` varchar(64) DEFAULT NULL COMMENT '本地APP版本信息',
+  `sl_version` varchar(64) DEFAULT NULL COMMENT '从板软件版本',
+  `sl_version_json` json DEFAULT NULL COMMENT 'sl版本JSON存储',
+  `meter_version` varchar(255) DEFAULT NULL COMMENT '电表版本',
+  `mb_version` varchar(255) DEFAULT NULL COMMENT 'Modbus软件版本',
+  `distributor_name` varchar(100) DEFAULT NULL COMMENT '分销商公司名称',
+  `installer_name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '安装商公司名称',
+  `maint_name` varchar(100) DEFAULT NULL COMMENT '维护商公司名称',
+  `account_name` varchar(32) DEFAULT NULL COMMENT '用户昵称',
+  `weather_county` varchar(128) DEFAULT NULL COMMENT 'weather 天气 对应的县ID(zoneId)',
+  `weather_state` varchar(128) DEFAULT NULL COMMENT 'weather 天气 对应的州(area)',
+  `elecnet_status` int DEFAULT NULL COMMENT '市电网状态 0:异常;1:正常',
+  `slaver_stat` int DEFAULT NULL COMMENT 'EMS当前运行状态:  0:初始化,1:参数获取;2:监听;3:待机;4:市电并网运行;5:离网运行;6:油机并网运行 7:故障',
+  `post_code` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '邮编',
+  `country` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '国家',
+  `province` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '省',
+  `city` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '市英文',
+  `protocol_ver` varchar(255) DEFAULT NULL COMMENT '协议版本号',
+  `gen_en` int DEFAULT NULL COMMENT '发电机接入使能(默认-0不使能 1使能)',
+  `gen_stat` int DEFAULT NULL COMMENT '发电机状态(0不使能 1停机 2启动 3运行 4退出 5故障)',
+  `main_sw` varchar(30) DEFAULT NULL COMMENT '主开关状态(1,0,1 备电主开关 发电机主开关 光伏主开关 0-断开 1-闭合 数据来源201)',
+  `installer_account` varchar(255) DEFAULT NULL COMMENT '安装商账户',
+  `wifi_signal` varchar(8) DEFAULT NULL COMMENT 'wifi信号强度',
+  `conn_type` int DEFAULT NULL COMMENT '连接方式(0:未接入网络 1:eth0 2:eth1 3:wifi 4:4G)',
+  `installer_account_id` int DEFAULT NULL COMMENT '安装商账户ID',
+  `load_option` tinyint unsigned DEFAULT '2' COMMENT '智能负载选配  0:禁止使能,1:使能,2:标配',
+  `ger_option` tinyint(1) DEFAULT '2' COMMENT '油机选配,0:禁止使能,1使能,默认出厂为0',
+  `equip_type` int DEFAULT NULL COMMENT '设备归属(2sunnova)',
+  `on_off_status` int DEFAULT NULL COMMENT '并网离网:1:并网  0 离网',
+  `country_id` int DEFAULT NULL COMMENT '国家编号,0:全部  1:中国  2:美国',
+  `province_id` int DEFAULT NULL COMMENT '省份编号',
+  `city_zh` varchar(32) DEFAULT NULL COMMENT '城市中文',
+  `financier_id` int DEFAULT NULL COMMENT '分销商大客户ID',
+  `financier_name` varchar(64) DEFAULT NULL COMMENT '分销商大客户名称',
+  `sys_hd_version` int DEFAULT NULL COMMENT '硬件版本号',
+  `resource_list` varchar(2000) DEFAULT NULL COMMENT '允许操作列表',
+  `city_id` int DEFAULT NULL COMMENT '城市id',
+  `financier_contract_number` varchar(64) DEFAULT NULL COMMENT '分销商大客户合同编号',
+  `region` varchar(255) DEFAULT NULL COMMENT 'APP填写的(国家、省、城市)',
+  `remark` varchar(2000) DEFAULT NULL COMMENT '备注信息',
+  `address_second_last` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '街道地址(前端根据谷歌接口模糊查询出来的)',
+  `ems_device_ver` int DEFAULT NULL COMMENT '默认为0,0:1.0设备类型  1:nsc设备类型',
+  `site` varchar(255) DEFAULT NULL COMMENT '站点',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `sort` (`sort`) USING BTREE,
+  KEY `index_country_province_city` (`country`,`province`,`city`),
+  KEY `idx_installer` (`installer_id`,`active_status`,`active_time`) USING BTREE,
+  KEY `idx_zone_info` (`zone_info`) USING BTREE,
+  KEY `idx_installer_account_id` (`installer_account_id`) USING BTREE,
+  KEY `idx_account` (`account`) USING BTREE,
+  KEY `ix_installer_id` (`installer_id`),
+  KEY `ix_account` (`account`),
+  KEY `ix_equip_type` (`equip_type`)
+) ENGINE=InnoDB AUTO_INCREMENT=541341 DEFAULT CHARSET=utf8mb3 COMMENT='IBG网关信息';
+/*!40101 SET character_set_client = @saved_cs_client */;
+SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2025-08-11 15:27:40

+ 23 - 0
service/database/sql/queryDev.sql

@@ -0,0 +1,23 @@
+SELECT * 
+FROM iot_gateway 
+WHERE active_status = 1 
+  AND country = 'United States'
+  AND province = 'California'
+  AND version LIKE 'V10R10B30D00%' 
+  AND run_status BETWEEN 0 AND 2 
+  AND elecnet_status = 0
+  AND id LIKE '1006%'
+--   AND (
+--     (
+--         -- 模糊匹配 '10050011' 开头的字符串
+-- 		fhp_sn LIKE '10050011%'
+-- 		OR fhp_sn LIKE '%,10050011%'
+--     ) AND (
+--       	-- 同时匹配 '10050015' 或 '10050018' 作为开头的字符串
+--     	fhp_sn LIKE '10050015%'
+-- 		OR fhp_sn LIKE '%,10050015%'
+--       	OR fhp_sn LIKE '10050018%'
+-- 		OR fhp_sn LIKE '%,10050018%'
+--     )
+--   )
+LIMIT 100

+ 1 - 0
service/database/sql/queryDistinct.sql

@@ -0,0 +1 @@
+SELECT DISTINCT country FROM iot_gateway LIMIT 100;

+ 1 - 0
service/docs/docs.go

@@ -0,0 +1 @@
+package docs

+ 300 - 0
service/ext/api/fwh_pro/fwh_pro.go

@@ -0,0 +1,300 @@
+package fwh_pro
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var loginInfo LoginData = LoginData{
+	UserId: 0,
+	Token:  "",
+}
+
+type LoginPayload struct {
+	LoginName string `json:"loginName"`
+	Password  string `json:"password"`
+	Type      int    `json:"type"`
+	Lang      string `json:"lang"`
+}
+
+type LoginData struct {
+	UserId uint   `json:"userId"`
+	Token  string `json:"token"`
+}
+
+type LoginResponse struct {
+	Code    int       `json:"code"`
+	Msg     string    `json:"msg"`
+	Data    LoginData `json:"data"`
+	Success bool      `json:"success"`
+}
+
+type SiteInfoListPayload struct {
+	Search        *string `json:"search,omitempty"`  // 只能搜索单条SN
+	Serials       *string `json:"serials,omitempty"` // 可搜索多条SN,SN之间以‘,’隔开
+	CountryId     *int    `json:"countryId,omitempty"`
+	ProvinceId    *int    `json:"provinceId,omitempty"`
+	CityId        *int    `json:"cityId,omitempty"`
+	NetworkStatus *int    `json:"networkStatus,omitempty"`
+	DeviceStatus  *int    `json:"deviceStatus,omitempty"`
+	OnOffStatus   *int    `json:"onOffStatus,omitempty"`
+	PageSize      uint    `json:"pageSize"`
+	PageNum       uint    `json:"pageNum"`
+	Lang          string  `json:"lang"`
+}
+
+type SiteModeInfoPayload struct {
+	GatewayId string `json:"gatewayId"`
+	ShowType  int    `json:"showType"`
+	Lang      string `json:"lang"`
+}
+
+func Login() error {
+	// 要发送的 JSON
+	payload := LoginPayload{
+		LoginName: "FMH001",
+		Password:  "cfd9d803fafcf2c7a832bfba5d039512",
+		Type:      1,
+		Lang:      "ZH_CN",
+	}
+
+	jsonData, _ := json.Marshal(payload)
+
+	// 创建请求
+	req, err := http.NewRequest(
+		http.MethodPost,
+		"https://oss.franklinwh.com/api-user/user/login",
+		bytes.NewBuffer(jsonData))
+	if err != nil {
+		fmt.Println(err.Error())
+		return err
+	}
+
+	// 设置 Content-Type 和 Authorization
+	req.Header.Set("Content-Type", "application/json")
+
+	// 发送请求
+	client := &http.Client{Timeout: 5 * time.Second}
+	resp, err := client.Do(req)
+	if err != nil {
+		fmt.Println(err.Error())
+		return err
+	}
+	defer resp.Body.Close()
+
+	body, _ := io.ReadAll(resp.Body)
+	// fmt.Printf("Status: %s\nBody: %s", resp.Status, body)
+
+	var loginRes LoginResponse
+	// 反序列化(unmarshal)
+	jsonErr := json.Unmarshal(body, &loginRes)
+	if jsonErr != nil {
+		return jsonErr
+	}
+
+	loginInfo.UserId = loginRes.Data.UserId
+	loginInfo.Token = loginRes.Data.Token
+
+	return nil
+}
+
+func isTokenExpired(token string) (bool, error) {
+	parts := strings.Split(token, ".")
+	if len(parts) != 3 {
+		return false, fmt.Errorf("token 格式错误")
+	}
+
+	payloadSegment := parts[1]
+	// JWT 使用 base64 URL encoding
+	payloadBytes, err := base64.RawURLEncoding.DecodeString(payloadSegment)
+	if err != nil {
+		return false, err
+	}
+
+	var payload map[string]interface{}
+	if err := json.Unmarshal(payloadBytes, &payload); err != nil {
+		return false, err
+	}
+
+	expFloat, ok := payload["exp"].(float64)
+	if !ok {
+		return false, fmt.Errorf("token payload 中没有 exp")
+	}
+
+	expTime := time.Unix(int64(expFloat), 0)
+	return time.Now().After(expTime), nil
+}
+
+func getLoginToken() string {
+	// 判断是否已经有token
+	if loginInfo.Token != "" {
+		// 判断token是否过期
+		expired, err := isTokenExpired(loginInfo.Token)
+		if err != nil {
+			fmt.Println("loginInfo.Token 解析出错:", err)
+			return ""
+		}
+
+		if expired {
+			fmt.Println("Token 已过期")
+		} else {
+			// fmt.Println("Token 有效")
+			return loginInfo.Token
+		}
+	}
+
+	// 重新登录
+	Login()
+	return loginInfo.Token
+}
+
+// 清空token
+func ClearToken() {
+	loginInfo.UserId = 0
+	loginInfo.Token = ""
+}
+
+func QuerySiteInfoList(sn []string, pageSize uint, pageNum uint) ([]byte, error) {
+	snList := strings.Join(sn, ",")
+
+	token := getLoginToken()
+
+	// 要发送的 JSON
+	payload := SiteInfoListPayload{
+		Serials:  &snList,
+		PageSize: pageSize,
+		PageNum:  pageNum,
+		Lang:     "ZH_CN",
+	}
+
+	// 要发送的表单数据
+	form := url.Values{}
+	form.Set("serials", *payload.Serials) // *string 转 string
+	form.Set("pageSize", fmt.Sprintf("%d", payload.PageSize))
+	form.Set("pageNum", fmt.Sprintf("%d", payload.PageNum))
+	form.Set("lang", payload.Lang)
+
+	// 创建请求
+	req, err := http.NewRequest(
+		http.MethodPost,
+		"https://energy.franklinwh.com/hes-gateway/manage/selectIotFirmwareGatewayList",
+		strings.NewReader(form.Encode()))
+	if err != nil {
+		fmt.Println(err.Error())
+		return nil, err
+	}
+
+	// 设置 Content-Type 和 Authorization
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
+	req.Header.Set("Logintoken", token)
+
+	// 发送请求
+	client := &http.Client{Timeout: 5 * time.Second}
+	resp, err := client.Do(req)
+	if err != nil {
+		fmt.Println(err.Error())
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	body, _ := io.ReadAll(resp.Body)
+	// fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body)
+	return body, nil
+}
+
+func QuerySiteModeInfo(sn string) ([]byte, error) {
+	token := getLoginToken()
+
+	// 要发送的 JSON
+	payload := SiteModeInfoPayload{
+		GatewayId: sn,
+		ShowType:  1,
+		Lang:      "ZH_CN",
+	}
+
+	// 要发送的表单数据
+	form := url.Values{}
+	form.Set("gatewayId", payload.GatewayId)
+	form.Set("showType", strconv.Itoa(payload.ShowType))
+	form.Set("lang", payload.Lang)
+
+	// 创建请求
+	req, err := http.NewRequest(
+		http.MethodPost,
+		"https://energy.franklinwh.com/hes-gateway/manage/tou/getGatewayTouListV2",
+		strings.NewReader(form.Encode()))
+	if err != nil {
+		fmt.Println(err.Error())
+		return nil, err
+	}
+
+	// 设置 Content-Type 和 Authorization
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
+	req.Header.Set("Logintoken", token)
+
+	// fmt.Println(req.Header)
+
+	// 发送请求
+	client := &http.Client{Timeout: 5 * time.Second}
+	resp, err := client.Do(req)
+	if err != nil {
+		fmt.Println(err.Error())
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	body, _ := io.ReadAll(resp.Body)
+	// fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body)
+	return body, nil
+}
+
+func QuerySitePowerFlow(sn string, day time.Time) ([]byte, error) {
+	baseURL := "https://energy.franklinwh.com/api-energy/power/getFhpPowerByDayOld"
+
+	token := getLoginToken()
+
+	// 构造 query 参数
+	params := url.Values{}
+	params.Add("gatewayId", sn)
+	params.Add("dayTime", day.Format(time.DateOnly)) // 会自动 URL 编码
+
+	// 拼接到 URL
+	finalURL := baseURL + "?" + params.Encode()
+
+	// 创建请求
+	req, err := http.NewRequest(
+		http.MethodGet,
+		finalURL,
+		nil)
+	if err != nil {
+		fmt.Println(err.Error())
+		return nil, err
+	}
+
+	// 设置 Content-Type 和 Authorization
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Logintoken", token)
+
+	// fmt.Println(req.Header)
+
+	// 发送请求
+	client := &http.Client{Timeout: 5 * time.Second}
+	resp, err := client.Do(req)
+	if err != nil {
+		fmt.Println(err.Error())
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	body, _ := io.ReadAll(resp.Body)
+	// fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body)
+	return body, nil
+}

+ 81 - 0
service/go.mod

@@ -0,0 +1,81 @@
+module fwh-toolbox-server
+
+go 1.23.0
+
+toolchain go1.24.5
+
+require (
+	github.com/go-redis/redis/v8 v8.11.5
+	github.com/gofiber/fiber/v2 v2.52.9
+	github.com/gofiber/swagger v1.1.1
+	github.com/robfig/cron/v3 v3.0.1
+	github.com/spf13/viper v1.20.1
+	go.uber.org/zap v1.27.0
+	gorm.io/driver/mysql v1.6.0
+	gorm.io/driver/postgres v1.6.0
+	gorm.io/gorm v1.30.1
+)
+
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/KyleBanks/depth v1.2.1 // indirect
+	github.com/PuerkitoBio/purell v1.1.1 // indirect
+	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
+	github.com/andybalholm/brotli v1.1.0 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/fsnotify/fsnotify v1.8.0 // indirect
+	github.com/go-openapi/jsonpointer v0.19.5 // indirect
+	github.com/go-openapi/jsonreference v0.19.6 // indirect
+	github.com/go-openapi/spec v0.20.4 // indirect
+	github.com/go-openapi/swag v0.19.15 // indirect
+	github.com/go-sql-driver/mysql v1.9.3 // indirect
+	github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/jackc/pgpassfile v1.0.0 // indirect
+	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
+	github.com/jackc/pgx/v5 v5.6.0 // indirect
+	github.com/jackc/puddle/v2 v2.2.2 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/klauspost/compress v1.17.9 // indirect
+	github.com/mailru/easyjson v0.7.6 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/mattn/go-runewidth v0.0.16 // indirect
+	github.com/montanaflynn/stats v0.7.1 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+	github.com/rivo/uniseg v0.2.0 // indirect
+	github.com/sagikazarmark/locafero v0.7.0 // indirect
+	github.com/sourcegraph/conc v0.3.0 // indirect
+	github.com/spf13/afero v1.12.0 // indirect
+	github.com/spf13/cast v1.7.1 // indirect
+	github.com/spf13/pflag v1.0.7 // indirect
+	github.com/subosito/gotenv v1.6.0 // indirect
+	github.com/swaggo/files/v2 v2.0.2 // indirect
+	github.com/swaggo/swag v1.16.4 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
+	github.com/valyala/fasthttp v1.51.0 // indirect
+	github.com/valyala/tcplisten v1.0.0 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.2 // indirect
+	github.com/xdg-go/stringprep v1.0.4 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
+	go.mongodb.org/mongo-driver v1.17.4 // indirect
+	go.uber.org/multierr v1.10.0 // indirect
+	golang.org/x/crypto v0.41.0 // indirect
+	golang.org/x/mod v0.27.0 // indirect
+	golang.org/x/net v0.43.0 // indirect
+	golang.org/x/sync v0.16.0 // indirect
+	golang.org/x/sys v0.35.0 // indirect
+	golang.org/x/text v0.28.0 // indirect
+	golang.org/x/tools v0.36.0 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	gorm.io/datatypes v1.2.6 // indirect
+	gorm.io/gen v0.3.27 // indirect
+	gorm.io/hints v1.1.2 // indirect
+	gorm.io/plugin/dbresolver v1.6.2 // indirect
+)

+ 235 - 0
service/go.sum

@@ -0,0 +1,235 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
+github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
+github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
+github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
+github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
+github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
+github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
+github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
+github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
+github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
+github.com/gofiber/swagger v1.1.1 h1:FZVhVQQ9s1ZKLHL/O0loLh49bYB5l1HEAgxDlcTtkRA=
+github.com/gofiber/swagger v1.1.1/go.mod h1:vtvY/sQAMc/lGTUCg0lqmBL7Ht9O7uzChpbvJeJQINw=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
+github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
+github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
+github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
+github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
+github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
+github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
+github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
+github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
+github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
+github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
+github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
+github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
+github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
+github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
+github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=
+github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
+github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
+github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
+github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
+github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
+github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
+go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
+go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
+golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
+golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
+golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
+golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
+golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/datatypes v1.2.6 h1:KafLdXvFUhzNeL2ncm03Gl3eTLONQfNKZ+wJ+9Y4Nck=
+gorm.io/datatypes v1.2.6/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
+gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
+gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
+gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
+gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
+gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
+gorm.io/gen v0.3.27 h1:ziocAFLpE7e0g4Rum69pGfB9S6DweTxK8gAun7cU8as=
+gorm.io/gen v0.3.27/go.mod h1:9zquz2xD1f3Eb/eHq4oLn2z6vDVvQlCY5S3uMBLv4EA=
+gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
+gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
+gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
+gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
+gorm.io/plugin/dbresolver v1.6.2 h1:F4b85TenghUeITqe3+epPSUtHH7RIk3fXr5l83DF8Pc=
+gorm.io/plugin/dbresolver v1.6.2/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM=

+ 17 - 0
service/logger/logger.go

@@ -0,0 +1,17 @@
+package logger
+
+import (
+  "go.uber.org/zap"
+)
+
+var Log *zap.Logger
+
+func Init(env string) error {
+  var err error
+  if env == "prod" {
+    Log, err = zap.NewProduction()
+  } else {
+    Log, err = zap.NewDevelopment()
+  }
+  return err
+}

+ 37 - 0
service/main.go

@@ -0,0 +1,37 @@
+package main
+
+import (
+	"fwh-toolbox-server/cache"
+	"fwh-toolbox-server/config"
+	"fwh-toolbox-server/database"
+
+	// "fwh-toolbox-server/database"
+	_ "fwh-toolbox-server/docs" // swag 自动生成文档
+	"fwh-toolbox-server/logger"
+	"fwh-toolbox-server/routes"
+	"fwh-toolbox-server/scheduler"
+
+	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/swagger"
+	"go.uber.org/zap"
+)
+
+func main() {
+	cfg, _ := config.LoadConfig()
+	logger.Init(cfg.AppEnv)
+	database.Init(cfg)
+	cache.Init(cfg.RedisAddr)
+	scheduler.Init()
+
+	app := fiber.New()
+	app.Use(func(c *fiber.Ctx) error {
+		logger.Log.Info("请求", zap.String("path", c.Path()))
+		return c.Next()
+	})
+
+	// 注册 Swagger 路由
+	app.Get("/swagger/*", swagger.HandlerDefault)
+
+	routes.Setup(app)
+	app.Listen("localhost:" + cfg.AppPort)
+}

+ 100 - 0
service/models/fwh_pro/model/iot_gateway.gen.go

@@ -0,0 +1,100 @@
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+
+package model
+
+import (
+	"time"
+)
+
+const TableNameIotGateway = "iot_gateway"
+
+// IotGateway IBG网关信息
+type IotGateway struct {
+	ID                      string    `gorm:"column:id;primaryKey;comment:主键ID(网关序列号)" json:"id"`                                                                                                                // 主键ID(网关序列号)
+	Name                    string    `gorm:"column:name;comment:网关名称(站点、可自定义)" json:"name"`                                                                                                                     // 网关名称(站点、可自定义)
+	Version                 string    `gorm:"column:version;comment:版本" json:"version"`                                                                                                                          // 版本
+	DistributorID           int32     `gorm:"column:distributor_id;comment:分销商公司ID" json:"distributor_id"`                                                                                                       // 分销商公司ID
+	InstallerID             int32     `gorm:"column:installer_id;comment:安装商公司ID" json:"installer_id"`                                                                                                           // 安装商公司ID
+	MaintID                 int32     `gorm:"column:maint_id;comment:维护商公司ID" json:"maint_id"`                                                                                                                   // 维护商公司ID
+	Account                 string    `gorm:"column:account;comment:绑定用户账号(邮箱)" json:"account"`                                                                                                                  // 绑定用户账号(邮箱)
+	Longitude               string    `gorm:"column:longitude;comment:安装地理信息(经度)" json:"longitude"`                                                                                                              // 安装地理信息(经度)
+	Latitude                string    `gorm:"column:latitude;comment:安装地理信息(纬度)" json:"latitude"`                                                                                                                // 安装地理信息(纬度)
+	GpsAddress              string    `gorm:"column:gps_address;comment:通过谷歌地图api解析经纬度出来的地址" json:"gps_address"`                                                                                                 // 通过谷歌地图api解析经纬度出来的地址
+	Address                 string    `gorm:"column:address;comment:安装地理信息(详细地址)(app生成并允许用户输入的详细地址)" json:"address"`                                                                                             // 安装地理信息(详细地址)(app生成并允许用户输入的详细地址)
+	ActiveStatus            int32     `gorm:"column:active_status;comment:激活状态(0未激活 1已激活)" json:"active_status"`                                                                                                 // 激活状态(0未激活 1已激活)
+	ActiveTime              time.Time `gorm:"column:active_time;comment:激活时间" json:"active_time"`                                                                                                                // 激活时间
+	LocalActiveTime         time.Time `gorm:"column:local_active_time;comment:设备当地激活时间" json:"local_active_time"`                                                                                                // 设备当地激活时间
+	Status                  int32     `gorm:"column:status;comment:网关状态(0离线 1正常 )" json:"status"`                                                                                                                // 网关状态(0离线 1正常 )
+	OnLineTime              time.Time `gorm:"column:on_line_time;comment:在线时间" json:"on_line_time"`                                                                                                              // 在线时间
+	OffLineTime             string    `gorm:"column:off_line_time;comment:离线时间" json:"off_line_time"`                                                                                                            // 离线时间
+	Model                   string    `gorm:"column:model;comment:型号" json:"model"`                                                                                                                              // 型号
+	DeviceIP                string    `gorm:"column:device_ip;comment:设备IP地址" json:"device_ip"`                                                                                                                  // 设备IP地址
+	Subnet                  string    `gorm:"column:subnet;comment:子网掩码" json:"subnet"`                                                                                                                          // 子网掩码
+	Mac                     string    `gorm:"column:mac;comment:MAC" json:"mac"`                                                                                                                                 // MAC
+	GatewayIP               string    `gorm:"column:gateway_ip;comment:网关IP地址" json:"gateway_ip"`                                                                                                                // 网关IP地址
+	Signal                  string    `gorm:"column:signal;comment:信号强度" json:"signal"`                                                                                                                          // 信号强度
+	IccID                   string    `gorm:"column:icc_id;comment:ICCID" json:"icc_id"`                                                                                                                         // ICCID
+	Imei                    string    `gorm:"column:imei;comment:IMEI" json:"imei"`                                                                                                                              // IMEI
+	FirmUpgradeTime         time.Time `gorm:"column:firm_upgrade_time;comment:最后固件升级时间" json:"firm_upgrade_time"`                                                                                                // 最后固件升级时间
+	ControlJSON             string    `gorm:"column:control_json;comment:控制JSON" json:"control_json"`                                                                                                            // 控制JSON
+	TurnoverTime            time.Time `gorm:"column:turnover_time;comment:更新时间" json:"turnover_time"`                                                                                                            // 更新时间
+	CreateTime              time.Time `gorm:"column:create_time;comment:创建时间" json:"create_time"`                                                                                                                // 创建时间
+	RunStatus               int32     `gorm:"column:run_status;comment:EMS 当前运行状态 0:待机;1:充电;2:放电;3:故障" json:"run_status"`                                                                                        // EMS 当前运行状态 0:待机;1:充电;2:放电;3:故障
+	EmsModel                int32     `gorm:"column:ems_model;comment:EMS 当前工作模式 1:自发自用;2:TOU(经济);3:TOU(平衡);4:等待备电;5:等待风暴;6:离网电池备电;7:离网发电机备电;8:VPP 调度;9:紧急停止;10:维护停止;11:复位自检;89:调试模式;99:故障停止。" json:"ems_model"` // EMS 当前工作模式 1:自发自用;2:TOU(经济);3:TOU(平衡);4:等待备电;5:等待风暴;6:离网电池备电;7:离网发电机备电;8:VPP 调度;9:紧急停止;10:维护停止;11:复位自检;89:调试模式;99:故障停止。
+	ElectricityType         int32     `gorm:"column:electricity_type;comment:电费类型.1:分时电价;2:阶梯电价;3:固定电价;4:分时&阶梯,5:BB 套餐 6:peak demand" json:"electricity_type"`                                                   // 电费类型.1:分时电价;2:阶梯电价;3:固定电价;4:分时&阶梯,5:BB 套餐 6:peak demand
+	FhpSn                   string    `gorm:"column:fhp_sn;comment:FHP 设备编号 INFI设备数组" json:"fhp_sn"`                                                                                                             // FHP 设备编号 INFI设备数组
+	Sort                    int32     `gorm:"column:sort;not null;comment:自增长序列排序" json:"sort"`                                                                                                                  // 自增长序列排序
+	DeviceControlData       string    `gorm:"column:device_control_data;comment:IOT-设备控制设置" json:"device_control_data"`                                                                                          // IOT-设备控制设置
+	TouParamSettingData     string    `gorm:"column:tou_param_setting_data;comment:IOT-TOU参数配置" json:"tou_param_setting_data"`                                                                                   // IOT-TOU参数配置
+	TimeZone                float32   `gorm:"column:time_zone;default:8.0;comment:时区" json:"time_zone"`                                                                                                          // 时区
+	ZoneInfo                string    `gorm:"column:zone_info;comment:地区代表时区。例如:America/New_York" json:"zone_info"`                                                                                              // 地区代表时区。例如:America/New_York
+	Dst                     int32     `gorm:"column:dst;comment:0:协调世界时间-N,1:自动调整夏令时" json:"dst"`                                                                                                                // 0:协调世界时间-N,1:自动调整夏令时
+	IbgMainVersion          string    `gorm:"column:ibg_main_version;comment:nsc支持,main_ver版本信息" json:"ibg_main_version"`                                                                                        // nsc支持,main_ver版本信息
+	AwsVersion              string    `gorm:"column:aws_version;comment:云平台版本信息" json:"aws_version"`                                                                                                             // 云平台版本信息
+	AppVersion              string    `gorm:"column:app_version;comment:本地APP版本信息" json:"app_version"`                                                                                                           // 本地APP版本信息
+	SlVersion               string    `gorm:"column:sl_version;comment:从板软件版本" json:"sl_version"`                                                                                                                // 从板软件版本
+	SlVersionJSON           string    `gorm:"column:sl_version_json;comment:sl版本JSON存储" json:"sl_version_json"`                                                                                                  // sl版本JSON存储
+	MeterVersion            string    `gorm:"column:meter_version;comment:电表版本" json:"meter_version"`                                                                                                            // 电表版本
+	MbVersion               string    `gorm:"column:mb_version;comment:Modbus软件版本" json:"mb_version"`                                                                                                            // Modbus软件版本
+	DistributorName         string    `gorm:"column:distributor_name;comment:分销商公司名称" json:"distributor_name"`                                                                                                   // 分销商公司名称
+	InstallerName           string    `gorm:"column:installer_name;comment:安装商公司名称" json:"installer_name"`                                                                                                       // 安装商公司名称
+	MaintName               string    `gorm:"column:maint_name;comment:维护商公司名称" json:"maint_name"`                                                                                                               // 维护商公司名称
+	AccountName             string    `gorm:"column:account_name;comment:用户昵称" json:"account_name"`                                                                                                              // 用户昵称
+	ElecnetStatus           int32     `gorm:"column:elecnet_status;comment:市电网状态 0:异常;1:正常" json:"elecnet_status"`                                                                                               // 市电网状态 0:异常;1:正常
+	SlaverStat              int32     `gorm:"column:slaver_stat;comment:EMS当前运行状态:  0:初始化,1:参数获取;2:监听;3:待机;4:市电并网运行;5:离网运行;6:油机并网运行 7:故障" json:"slaver_stat"`                                                    // EMS当前运行状态:  0:初始化,1:参数获取;2:监听;3:待机;4:市电并网运行;5:离网运行;6:油机并网运行 7:故障
+	PostCode                string    `gorm:"column:post_code;comment:邮编" json:"post_code"`                                                                                                                      // 邮编
+	Country                 string    `gorm:"column:country;comment:国家" json:"country"`                                                                                                                          // 国家
+	Province                string    `gorm:"column:province;comment:省" json:"province"`                                                                                                                         // 省
+	City                    string    `gorm:"column:city;comment:市英文" json:"city"`                                                                                                                               // 市英文
+	ProtocolVer             string    `gorm:"column:protocol_ver;comment:协议版本号" json:"protocol_ver"`                                                                                                             // 协议版本号
+	GenEn                   int32     `gorm:"column:gen_en;comment:发电机接入使能(默认-0不使能 1使能)" json:"gen_en"`                                                                                                          // 发电机接入使能(默认-0不使能 1使能)
+	InstallerAccount        string    `gorm:"column:installer_account;comment:安装商账户" json:"installer_account"`                                                                                                   // 安装商账户
+	WifiSignal              string    `gorm:"column:wifi_signal;comment:wifi信号强度" json:"wifi_signal"`                                                                                                            // wifi信号强度
+	ConnType                int32     `gorm:"column:conn_type;comment:连接方式(0:未接入网络 1:eth0 2:eth1 3:wifi 4:4G)" json:"conn_type"`                                                                                 // 连接方式(0:未接入网络 1:eth0 2:eth1 3:wifi 4:4G)
+	InstallerAccountID      int32     `gorm:"column:installer_account_id;comment:安装商账户ID" json:"installer_account_id"`                                                                                           // 安装商账户ID
+	LoadOption              int32     `gorm:"column:load_option;default:2;comment:智能负载选配  0:禁止使能,1:使能,2:标配" json:"load_option"`                                                                                  // 智能负载选配  0:禁止使能,1:使能,2:标配
+	GerOption               bool      `gorm:"column:ger_option;default:2;comment:油机选配,0:禁止使能,1使能,默认出厂为0" json:"ger_option"`                                                                                      // 油机选配,0:禁止使能,1使能,默认出厂为0
+	EquipType               int32     `gorm:"column:equip_type;comment:设备归属(2sunnova)" json:"equip_type"`                                                                                                        // 设备归属(2sunnova)
+	OnOffStatus             int32     `gorm:"column:on_off_status;comment:并网离网:1:并网  0 离网" json:"on_off_status"`                                                                                                 // 并网离网:1:并网  0 离网
+	CountryID               int32     `gorm:"column:country_id;comment:国家编号,0:全部  1:中国  2:美国" json:"country_id"`                                                                                                 // 国家编号,0:全部  1:中国  2:美国
+	ProvinceID              int32     `gorm:"column:province_id;comment:省份编号" json:"province_id"`                                                                                                                // 省份编号
+	CityZh                  string    `gorm:"column:city_zh;comment:城市中文" json:"city_zh"`                                                                                                                        // 城市中文
+	FinancierID             int32     `gorm:"column:financier_id;comment:分销商大客户ID" json:"financier_id"`                                                                                                          // 分销商大客户ID
+	FinancierName           string    `gorm:"column:financier_name;comment:分销商大客户名称" json:"financier_name"`                                                                                                      // 分销商大客户名称
+	SysHdVersion            int32     `gorm:"column:sys_hd_version;comment:硬件版本号" json:"sys_hd_version"`                                                                                                         // 硬件版本号
+	ResourceList            string    `gorm:"column:resource_list;comment:允许操作列表" json:"resource_list"`                                                                                                          // 允许操作列表
+	CityID                  int32     `gorm:"column:city_id;comment:城市id" json:"city_id"`                                                                                                                        // 城市id
+	FinancierContractNumber string    `gorm:"column:financier_contract_number;comment:分销商大客户合同编号" json:"financier_contract_number"`                                                                              // 分销商大客户合同编号
+	Region                  string    `gorm:"column:region;comment:APP填写的(国家、省、城市)" json:"region"`                                                                                                               // APP填写的(国家、省、城市)
+	Remark                  string    `gorm:"column:remark;comment:备注信息" json:"remark"`                                                                                                                          // 备注信息
+	AddressSecondLast       string    `gorm:"column:address_second_last;comment:街道地址(前端根据谷歌接口模糊查询出来的)" json:"address_second_last"`                                                                               // 街道地址(前端根据谷歌接口模糊查询出来的)
+	EmsDeviceVer            int32     `gorm:"column:ems_device_ver;comment:默认为0,0:1.0设备类型  1:nsc设备类型" json:"ems_device_ver"`                                                                                     // 默认为0,0:1.0设备类型  1:nsc设备类型
+	Site                    string    `gorm:"column:site;comment:站点" json:"site"`                                                                                                                                // 站点
+}
+
+// TableName IotGateway's table name
+func (*IotGateway) TableName() string {
+	return TableNameIotGateway
+}

+ 103 - 0
service/models/fwh_pro/query/gen.go

@@ -0,0 +1,103 @@
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+
+package query
+
+import (
+	"context"
+	"database/sql"
+
+	"gorm.io/gorm"
+
+	"gorm.io/gen"
+
+	"gorm.io/plugin/dbresolver"
+)
+
+var (
+	Q          = new(Query)
+	IotGateway *iotGateway
+)
+
+func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
+	*Q = *Use(db, opts...)
+	IotGateway = &Q.IotGateway
+}
+
+func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
+	return &Query{
+		db:         db,
+		IotGateway: newIotGateway(db, opts...),
+	}
+}
+
+type Query struct {
+	db *gorm.DB
+
+	IotGateway iotGateway
+}
+
+func (q *Query) Available() bool { return q.db != nil }
+
+func (q *Query) clone(db *gorm.DB) *Query {
+	return &Query{
+		db:         db,
+		IotGateway: q.IotGateway.clone(db),
+	}
+}
+
+func (q *Query) ReadDB() *Query {
+	return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
+}
+
+func (q *Query) WriteDB() *Query {
+	return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
+}
+
+func (q *Query) ReplaceDB(db *gorm.DB) *Query {
+	return &Query{
+		db:         db,
+		IotGateway: q.IotGateway.replaceDB(db),
+	}
+}
+
+type queryCtx struct {
+	IotGateway IIotGatewayDo
+}
+
+func (q *Query) WithContext(ctx context.Context) *queryCtx {
+	return &queryCtx{
+		IotGateway: q.IotGateway.WithContext(ctx),
+	}
+}
+
+func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
+	return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
+}
+
+func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
+	tx := q.db.Begin(opts...)
+	return &QueryTx{Query: q.clone(tx), Error: tx.Error}
+}
+
+type QueryTx struct {
+	*Query
+	Error error
+}
+
+func (q *QueryTx) Commit() error {
+	return q.db.Commit().Error
+}
+
+func (q *QueryTx) Rollback() error {
+	return q.db.Rollback().Error
+}
+
+func (q *QueryTx) SavePoint(name string) error {
+	return q.db.SavePoint(name).Error
+}
+
+func (q *QueryTx) RollbackTo(name string) error {
+	return q.db.RollbackTo(name).Error
+}

+ 710 - 0
service/models/fwh_pro/query/iot_gateway.gen.go

@@ -0,0 +1,710 @@
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+
+package query
+
+import (
+	"context"
+	"database/sql"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"gorm.io/gorm/schema"
+
+	"gorm.io/gen"
+	"gorm.io/gen/field"
+
+	"gorm.io/plugin/dbresolver"
+
+	"fwh-toolbox-server/models/fwh_pro/model"
+)
+
+func newIotGateway(db *gorm.DB, opts ...gen.DOOption) iotGateway {
+	_iotGateway := iotGateway{}
+
+	_iotGateway.iotGatewayDo.UseDB(db, opts...)
+	_iotGateway.iotGatewayDo.UseModel(&model.IotGateway{})
+
+	tableName := _iotGateway.iotGatewayDo.TableName()
+	_iotGateway.ALL = field.NewAsterisk(tableName)
+	_iotGateway.ID = field.NewString(tableName, "id")
+	_iotGateway.Name = field.NewString(tableName, "name")
+	_iotGateway.Version = field.NewString(tableName, "version")
+	_iotGateway.DistributorID = field.NewInt32(tableName, "distributor_id")
+	_iotGateway.InstallerID = field.NewInt32(tableName, "installer_id")
+	_iotGateway.MaintID = field.NewInt32(tableName, "maint_id")
+	_iotGateway.Account = field.NewString(tableName, "account")
+	_iotGateway.Longitude = field.NewString(tableName, "longitude")
+	_iotGateway.Latitude = field.NewString(tableName, "latitude")
+	_iotGateway.GpsAddress = field.NewString(tableName, "gps_address")
+	_iotGateway.Address = field.NewString(tableName, "address")
+	_iotGateway.ActiveStatus = field.NewInt32(tableName, "active_status")
+	_iotGateway.ActiveTime = field.NewTime(tableName, "active_time")
+	_iotGateway.LocalActiveTime = field.NewTime(tableName, "local_active_time")
+	_iotGateway.Status = field.NewInt32(tableName, "status")
+	_iotGateway.OnLineTime = field.NewTime(tableName, "on_line_time")
+	_iotGateway.OffLineTime = field.NewString(tableName, "off_line_time")
+	_iotGateway.Model = field.NewString(tableName, "model")
+	_iotGateway.DeviceIP = field.NewString(tableName, "device_ip")
+	_iotGateway.Subnet = field.NewString(tableName, "subnet")
+	_iotGateway.Mac = field.NewString(tableName, "mac")
+	_iotGateway.GatewayIP = field.NewString(tableName, "gateway_ip")
+	_iotGateway.Signal = field.NewString(tableName, "signal")
+	_iotGateway.IccID = field.NewString(tableName, "icc_id")
+	_iotGateway.Imei = field.NewString(tableName, "imei")
+	_iotGateway.FirmUpgradeTime = field.NewTime(tableName, "firm_upgrade_time")
+	_iotGateway.ControlJSON = field.NewString(tableName, "control_json")
+	_iotGateway.TurnoverTime = field.NewTime(tableName, "turnover_time")
+	_iotGateway.CreateTime = field.NewTime(tableName, "create_time")
+	_iotGateway.RunStatus = field.NewInt32(tableName, "run_status")
+	_iotGateway.EmsModel = field.NewInt32(tableName, "ems_model")
+	_iotGateway.ElectricityType = field.NewInt32(tableName, "electricity_type")
+	_iotGateway.FhpSn = field.NewString(tableName, "fhp_sn")
+	_iotGateway.Sort = field.NewInt32(tableName, "sort")
+	_iotGateway.DeviceControlData = field.NewString(tableName, "device_control_data")
+	_iotGateway.TouParamSettingData = field.NewString(tableName, "tou_param_setting_data")
+	_iotGateway.TimeZone = field.NewFloat32(tableName, "time_zone")
+	_iotGateway.ZoneInfo = field.NewString(tableName, "zone_info")
+	_iotGateway.Dst = field.NewInt32(tableName, "dst")
+	_iotGateway.IbgMainVersion = field.NewString(tableName, "ibg_main_version")
+	_iotGateway.AwsVersion = field.NewString(tableName, "aws_version")
+	_iotGateway.AppVersion = field.NewString(tableName, "app_version")
+	_iotGateway.SlVersion = field.NewString(tableName, "sl_version")
+	_iotGateway.SlVersionJSON = field.NewString(tableName, "sl_version_json")
+	_iotGateway.MeterVersion = field.NewString(tableName, "meter_version")
+	_iotGateway.MbVersion = field.NewString(tableName, "mb_version")
+	_iotGateway.DistributorName = field.NewString(tableName, "distributor_name")
+	_iotGateway.InstallerName = field.NewString(tableName, "installer_name")
+	_iotGateway.MaintName = field.NewString(tableName, "maint_name")
+	_iotGateway.AccountName = field.NewString(tableName, "account_name")
+	_iotGateway.ElecnetStatus = field.NewInt32(tableName, "elecnet_status")
+	_iotGateway.SlaverStat = field.NewInt32(tableName, "slaver_stat")
+	_iotGateway.PostCode = field.NewString(tableName, "post_code")
+	_iotGateway.Country = field.NewString(tableName, "country")
+	_iotGateway.Province = field.NewString(tableName, "province")
+	_iotGateway.City = field.NewString(tableName, "city")
+	_iotGateway.ProtocolVer = field.NewString(tableName, "protocol_ver")
+	_iotGateway.GenEn = field.NewInt32(tableName, "gen_en")
+	_iotGateway.InstallerAccount = field.NewString(tableName, "installer_account")
+	_iotGateway.WifiSignal = field.NewString(tableName, "wifi_signal")
+	_iotGateway.ConnType = field.NewInt32(tableName, "conn_type")
+	_iotGateway.InstallerAccountID = field.NewInt32(tableName, "installer_account_id")
+	_iotGateway.LoadOption = field.NewInt32(tableName, "load_option")
+	_iotGateway.GerOption = field.NewInt8(tableName, "ger_option")
+	_iotGateway.EquipType = field.NewInt32(tableName, "equip_type")
+	_iotGateway.OnOffStatus = field.NewInt32(tableName, "on_off_status")
+	_iotGateway.CountryID = field.NewInt32(tableName, "country_id")
+	_iotGateway.ProvinceID = field.NewInt32(tableName, "province_id")
+	_iotGateway.CityZh = field.NewString(tableName, "city_zh")
+	_iotGateway.FinancierID = field.NewInt32(tableName, "financier_id")
+	_iotGateway.FinancierName = field.NewString(tableName, "financier_name")
+	_iotGateway.SysHdVersion = field.NewInt32(tableName, "sys_hd_version")
+	_iotGateway.ResourceList = field.NewString(tableName, "resource_list")
+	_iotGateway.CityID = field.NewInt32(tableName, "city_id")
+	_iotGateway.FinancierContractNumber = field.NewString(tableName, "financier_contract_number")
+	_iotGateway.Region = field.NewString(tableName, "region")
+	_iotGateway.Remark = field.NewString(tableName, "remark")
+	_iotGateway.AddressSecondLast = field.NewString(tableName, "address_second_last")
+	_iotGateway.EmsDeviceVer = field.NewInt32(tableName, "ems_device_ver")
+	_iotGateway.Site = field.NewString(tableName, "site")
+
+	_iotGateway.fillFieldMap()
+
+	return _iotGateway
+}
+
+// iotGateway IBG网关信息
+type iotGateway struct {
+	iotGatewayDo iotGatewayDo
+
+	ALL                     field.Asterisk
+	ID                      field.String  // 主键ID(网关序列号)
+	Name                    field.String  // 网关名称(站点、可自定义)
+	Version                 field.String  // 版本
+	DistributorID           field.Int32   // 分销商公司ID
+	InstallerID             field.Int32   // 安装商公司ID
+	MaintID                 field.Int32   // 维护商公司ID
+	Account                 field.String  // 绑定用户账号(邮箱)
+	Longitude               field.String  // 安装地理信息(经度)
+	Latitude                field.String  // 安装地理信息(纬度)
+	GpsAddress              field.String  // 通过谷歌地图api解析经纬度出来的地址
+	Address                 field.String  // 安装地理信息(详细地址)(app生成并允许用户输入的详细地址)
+	ActiveStatus            field.Int32   // 激活状态(0未激活 1已激活)
+	ActiveTime              field.Time    // 激活时间
+	LocalActiveTime         field.Time    // 设备当地激活时间
+	Status                  field.Int32   // 网关状态(0离线 1正常 )
+	OnLineTime              field.Time    // 在线时间
+	OffLineTime             field.String  // 离线时间
+	Model                   field.String  // 型号
+	DeviceIP                field.String  // 设备IP地址
+	Subnet                  field.String  // 子网掩码
+	Mac                     field.String  // MAC
+	GatewayIP               field.String  // 网关IP地址
+	Signal                  field.String  // 信号强度
+	IccID                   field.String  // ICCID
+	Imei                    field.String  // IMEI
+	FirmUpgradeTime         field.Time    // 最后固件升级时间
+	ControlJSON             field.String  // 控制JSON
+	TurnoverTime            field.Time    // 更新时间
+	CreateTime              field.Time    // 创建时间
+	RunStatus               field.Int32   // EMS 当前运行状态 0:待机;1:充电;2:放电;3:故障
+	EmsModel                field.Int32   // EMS 当前工作模式 1:自发自用;2:TOU(经济);3:TOU(平衡);4:等待备电;5:等待风暴;6:离网电池备电;7:离网发电机备电;8:VPP 调度;9:紧急停止;10:维护停止;11:复位自检;89:调试模式;99:故障停止。
+	ElectricityType         field.Int32   // 电费类型.1:分时电价;2:阶梯电价;3:固定电价;4:分时&阶梯,5:BB 套餐 6:peak demand
+	FhpSn                   field.String  // FHP 设备编号 INFI设备数组
+	Sort                    field.Int32   // 自增长序列排序
+	DeviceControlData       field.String  // IOT-设备控制设置
+	TouParamSettingData     field.String  // IOT-TOU参数配置
+	TimeZone                field.Float32 // 时区
+	ZoneInfo                field.String  // 地区代表时区。例如:America/New_York
+	Dst                     field.Int32   // 0:协调世界时间-N,1:自动调整夏令时
+	IbgMainVersion          field.String  // nsc支持,main_ver版本信息
+	AwsVersion              field.String  // 云平台版本信息
+	AppVersion              field.String  // 本地APP版本信息
+	SlVersion               field.String  // 从板软件版本
+	SlVersionJSON           field.String  // sl版本JSON存储
+	MeterVersion            field.String  // 电表版本
+	MbVersion               field.String  // Modbus软件版本
+	DistributorName         field.String  // 分销商公司名称
+	InstallerName           field.String  // 安装商公司名称
+	MaintName               field.String  // 维护商公司名称
+	AccountName             field.String  // 用户昵称
+	ElecnetStatus           field.Int32   // 市电网状态 0:异常;1:正常
+	SlaverStat              field.Int32   // EMS当前运行状态:  0:初始化,1:参数获取;2:监听;3:待机;4:市电并网运行;5:离网运行;6:油机并网运行 7:故障
+	PostCode                field.String  // 邮编
+	Country                 field.String  // 国家
+	Province                field.String  // 省
+	City                    field.String  // 市英文
+	ProtocolVer             field.String  // 协议版本号
+	GenEn                   field.Int32   // 发电机接入使能(默认-0不使能 1使能)
+	InstallerAccount        field.String  // 安装商账户
+	WifiSignal              field.String  // wifi信号强度
+	ConnType                field.Int32   // 连接方式(0:未接入网络 1:eth0 2:eth1 3:wifi 4:4G)
+	InstallerAccountID      field.Int32   // 安装商账户ID
+	LoadOption              field.Int32   // 智能负载选配  0:禁止使能,1:使能,2:标配
+	GerOption               field.Int8    // 油机选配,0:禁止使能,1使能,默认出厂为0
+	EquipType               field.Int32   // 设备归属(2sunnova)
+	OnOffStatus             field.Int32   // 并网离网:1:并网  0 离网
+	CountryID               field.Int32   // 国家编号,0:全部  1:中国  2:美国
+	ProvinceID              field.Int32   // 省份编号
+	CityZh                  field.String  // 城市中文
+	FinancierID             field.Int32   // 分销商大客户ID
+	FinancierName           field.String  // 分销商大客户名称
+	SysHdVersion            field.Int32   // 硬件版本号
+	ResourceList            field.String  // 允许操作列表
+	CityID                  field.Int32   // 城市id
+	FinancierContractNumber field.String  // 分销商大客户合同编号
+	Region                  field.String  // APP填写的(国家、省、城市)
+	Remark                  field.String  // 备注信息
+	AddressSecondLast       field.String  // 街道地址(前端根据谷歌接口模糊查询出来的)
+	EmsDeviceVer            field.Int32   // 默认为0,0:1.0设备类型  1:nsc设备类型
+	Site                    field.String  // 站点
+
+	fieldMap map[string]field.Expr
+}
+
+func (i iotGateway) Table(newTableName string) *iotGateway {
+	i.iotGatewayDo.UseTable(newTableName)
+	return i.updateTableName(newTableName)
+}
+
+func (i iotGateway) As(alias string) *iotGateway {
+	i.iotGatewayDo.DO = *(i.iotGatewayDo.As(alias).(*gen.DO))
+	return i.updateTableName(alias)
+}
+
+func (i *iotGateway) updateTableName(table string) *iotGateway {
+	i.ALL = field.NewAsterisk(table)
+	i.ID = field.NewString(table, "id")
+	i.Name = field.NewString(table, "name")
+	i.Version = field.NewString(table, "version")
+	i.DistributorID = field.NewInt32(table, "distributor_id")
+	i.InstallerID = field.NewInt32(table, "installer_id")
+	i.MaintID = field.NewInt32(table, "maint_id")
+	i.Account = field.NewString(table, "account")
+	i.Longitude = field.NewString(table, "longitude")
+	i.Latitude = field.NewString(table, "latitude")
+	i.GpsAddress = field.NewString(table, "gps_address")
+	i.Address = field.NewString(table, "address")
+	i.ActiveStatus = field.NewInt32(table, "active_status")
+	i.ActiveTime = field.NewTime(table, "active_time")
+	i.LocalActiveTime = field.NewTime(table, "local_active_time")
+	i.Status = field.NewInt32(table, "status")
+	i.OnLineTime = field.NewTime(table, "on_line_time")
+	i.OffLineTime = field.NewString(table, "off_line_time")
+	i.Model = field.NewString(table, "model")
+	i.DeviceIP = field.NewString(table, "device_ip")
+	i.Subnet = field.NewString(table, "subnet")
+	i.Mac = field.NewString(table, "mac")
+	i.GatewayIP = field.NewString(table, "gateway_ip")
+	i.Signal = field.NewString(table, "signal")
+	i.IccID = field.NewString(table, "icc_id")
+	i.Imei = field.NewString(table, "imei")
+	i.FirmUpgradeTime = field.NewTime(table, "firm_upgrade_time")
+	i.ControlJSON = field.NewString(table, "control_json")
+	i.TurnoverTime = field.NewTime(table, "turnover_time")
+	i.CreateTime = field.NewTime(table, "create_time")
+	i.RunStatus = field.NewInt32(table, "run_status")
+	i.EmsModel = field.NewInt32(table, "ems_model")
+	i.ElectricityType = field.NewInt32(table, "electricity_type")
+	i.FhpSn = field.NewString(table, "fhp_sn")
+	i.Sort = field.NewInt32(table, "sort")
+	i.DeviceControlData = field.NewString(table, "device_control_data")
+	i.TouParamSettingData = field.NewString(table, "tou_param_setting_data")
+	i.TimeZone = field.NewFloat32(table, "time_zone")
+	i.ZoneInfo = field.NewString(table, "zone_info")
+	i.Dst = field.NewInt32(table, "dst")
+	i.IbgMainVersion = field.NewString(table, "ibg_main_version")
+	i.AwsVersion = field.NewString(table, "aws_version")
+	i.AppVersion = field.NewString(table, "app_version")
+	i.SlVersion = field.NewString(table, "sl_version")
+	i.SlVersionJSON = field.NewString(table, "sl_version_json")
+	i.MeterVersion = field.NewString(table, "meter_version")
+	i.MbVersion = field.NewString(table, "mb_version")
+	i.DistributorName = field.NewString(table, "distributor_name")
+	i.InstallerName = field.NewString(table, "installer_name")
+	i.MaintName = field.NewString(table, "maint_name")
+	i.AccountName = field.NewString(table, "account_name")
+	i.ElecnetStatus = field.NewInt32(table, "elecnet_status")
+	i.SlaverStat = field.NewInt32(table, "slaver_stat")
+	i.PostCode = field.NewString(table, "post_code")
+	i.Country = field.NewString(table, "country")
+	i.Province = field.NewString(table, "province")
+	i.City = field.NewString(table, "city")
+	i.ProtocolVer = field.NewString(table, "protocol_ver")
+	i.GenEn = field.NewInt32(table, "gen_en")
+	i.InstallerAccount = field.NewString(table, "installer_account")
+	i.WifiSignal = field.NewString(table, "wifi_signal")
+	i.ConnType = field.NewInt32(table, "conn_type")
+	i.InstallerAccountID = field.NewInt32(table, "installer_account_id")
+	i.LoadOption = field.NewInt32(table, "load_option")
+	i.GerOption = field.NewInt8(table, "ger_option")
+	i.EquipType = field.NewInt32(table, "equip_type")
+	i.OnOffStatus = field.NewInt32(table, "on_off_status")
+	i.CountryID = field.NewInt32(table, "country_id")
+	i.ProvinceID = field.NewInt32(table, "province_id")
+	i.CityZh = field.NewString(table, "city_zh")
+	i.FinancierID = field.NewInt32(table, "financier_id")
+	i.FinancierName = field.NewString(table, "financier_name")
+	i.SysHdVersion = field.NewInt32(table, "sys_hd_version")
+	i.ResourceList = field.NewString(table, "resource_list")
+	i.CityID = field.NewInt32(table, "city_id")
+	i.FinancierContractNumber = field.NewString(table, "financier_contract_number")
+	i.Region = field.NewString(table, "region")
+	i.Remark = field.NewString(table, "remark")
+	i.AddressSecondLast = field.NewString(table, "address_second_last")
+	i.EmsDeviceVer = field.NewInt32(table, "ems_device_ver")
+	i.Site = field.NewString(table, "site")
+
+	i.fillFieldMap()
+
+	return i
+}
+
+func (i *iotGateway) WithContext(ctx context.Context) IIotGatewayDo {
+	return i.iotGatewayDo.WithContext(ctx)
+}
+
+func (i iotGateway) TableName() string { return i.iotGatewayDo.TableName() }
+
+func (i iotGateway) Alias() string { return i.iotGatewayDo.Alias() }
+
+func (i iotGateway) Columns(cols ...field.Expr) gen.Columns { return i.iotGatewayDo.Columns(cols...) }
+
+func (i *iotGateway) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
+	_f, ok := i.fieldMap[fieldName]
+	if !ok || _f == nil {
+		return nil, false
+	}
+	_oe, ok := _f.(field.OrderExpr)
+	return _oe, ok
+}
+
+func (i *iotGateway) fillFieldMap() {
+	i.fieldMap = make(map[string]field.Expr, 80)
+	i.fieldMap["id"] = i.ID
+	i.fieldMap["name"] = i.Name
+	i.fieldMap["version"] = i.Version
+	i.fieldMap["distributor_id"] = i.DistributorID
+	i.fieldMap["installer_id"] = i.InstallerID
+	i.fieldMap["maint_id"] = i.MaintID
+	i.fieldMap["account"] = i.Account
+	i.fieldMap["longitude"] = i.Longitude
+	i.fieldMap["latitude"] = i.Latitude
+	i.fieldMap["gps_address"] = i.GpsAddress
+	i.fieldMap["address"] = i.Address
+	i.fieldMap["active_status"] = i.ActiveStatus
+	i.fieldMap["active_time"] = i.ActiveTime
+	i.fieldMap["local_active_time"] = i.LocalActiveTime
+	i.fieldMap["status"] = i.Status
+	i.fieldMap["on_line_time"] = i.OnLineTime
+	i.fieldMap["off_line_time"] = i.OffLineTime
+	i.fieldMap["model"] = i.Model
+	i.fieldMap["device_ip"] = i.DeviceIP
+	i.fieldMap["subnet"] = i.Subnet
+	i.fieldMap["mac"] = i.Mac
+	i.fieldMap["gateway_ip"] = i.GatewayIP
+	i.fieldMap["signal"] = i.Signal
+	i.fieldMap["icc_id"] = i.IccID
+	i.fieldMap["imei"] = i.Imei
+	i.fieldMap["firm_upgrade_time"] = i.FirmUpgradeTime
+	i.fieldMap["control_json"] = i.ControlJSON
+	i.fieldMap["turnover_time"] = i.TurnoverTime
+	i.fieldMap["create_time"] = i.CreateTime
+	i.fieldMap["run_status"] = i.RunStatus
+	i.fieldMap["ems_model"] = i.EmsModel
+	i.fieldMap["electricity_type"] = i.ElectricityType
+	i.fieldMap["fhp_sn"] = i.FhpSn
+	i.fieldMap["sort"] = i.Sort
+	i.fieldMap["device_control_data"] = i.DeviceControlData
+	i.fieldMap["tou_param_setting_data"] = i.TouParamSettingData
+	i.fieldMap["time_zone"] = i.TimeZone
+	i.fieldMap["zone_info"] = i.ZoneInfo
+	i.fieldMap["dst"] = i.Dst
+	i.fieldMap["ibg_main_version"] = i.IbgMainVersion
+	i.fieldMap["aws_version"] = i.AwsVersion
+	i.fieldMap["app_version"] = i.AppVersion
+	i.fieldMap["sl_version"] = i.SlVersion
+	i.fieldMap["sl_version_json"] = i.SlVersionJSON
+	i.fieldMap["meter_version"] = i.MeterVersion
+	i.fieldMap["mb_version"] = i.MbVersion
+	i.fieldMap["distributor_name"] = i.DistributorName
+	i.fieldMap["installer_name"] = i.InstallerName
+	i.fieldMap["maint_name"] = i.MaintName
+	i.fieldMap["account_name"] = i.AccountName
+	i.fieldMap["elecnet_status"] = i.ElecnetStatus
+	i.fieldMap["slaver_stat"] = i.SlaverStat
+	i.fieldMap["post_code"] = i.PostCode
+	i.fieldMap["country"] = i.Country
+	i.fieldMap["province"] = i.Province
+	i.fieldMap["city"] = i.City
+	i.fieldMap["protocol_ver"] = i.ProtocolVer
+	i.fieldMap["gen_en"] = i.GenEn
+	i.fieldMap["installer_account"] = i.InstallerAccount
+	i.fieldMap["wifi_signal"] = i.WifiSignal
+	i.fieldMap["conn_type"] = i.ConnType
+	i.fieldMap["installer_account_id"] = i.InstallerAccountID
+	i.fieldMap["load_option"] = i.LoadOption
+	i.fieldMap["ger_option"] = i.GerOption
+	i.fieldMap["equip_type"] = i.EquipType
+	i.fieldMap["on_off_status"] = i.OnOffStatus
+	i.fieldMap["country_id"] = i.CountryID
+	i.fieldMap["province_id"] = i.ProvinceID
+	i.fieldMap["city_zh"] = i.CityZh
+	i.fieldMap["financier_id"] = i.FinancierID
+	i.fieldMap["financier_name"] = i.FinancierName
+	i.fieldMap["sys_hd_version"] = i.SysHdVersion
+	i.fieldMap["resource_list"] = i.ResourceList
+	i.fieldMap["city_id"] = i.CityID
+	i.fieldMap["financier_contract_number"] = i.FinancierContractNumber
+	i.fieldMap["region"] = i.Region
+	i.fieldMap["remark"] = i.Remark
+	i.fieldMap["address_second_last"] = i.AddressSecondLast
+	i.fieldMap["ems_device_ver"] = i.EmsDeviceVer
+	i.fieldMap["site"] = i.Site
+}
+
+func (i iotGateway) clone(db *gorm.DB) iotGateway {
+	i.iotGatewayDo.ReplaceConnPool(db.Statement.ConnPool)
+	return i
+}
+
+func (i iotGateway) replaceDB(db *gorm.DB) iotGateway {
+	i.iotGatewayDo.ReplaceDB(db)
+	return i
+}
+
+type iotGatewayDo struct{ gen.DO }
+
+type IIotGatewayDo interface {
+	gen.SubQuery
+	Debug() IIotGatewayDo
+	WithContext(ctx context.Context) IIotGatewayDo
+	WithResult(fc func(tx gen.Dao)) gen.ResultInfo
+	ReplaceDB(db *gorm.DB)
+	ReadDB() IIotGatewayDo
+	WriteDB() IIotGatewayDo
+	As(alias string) gen.Dao
+	Session(config *gorm.Session) IIotGatewayDo
+	Columns(cols ...field.Expr) gen.Columns
+	Clauses(conds ...clause.Expression) IIotGatewayDo
+	Not(conds ...gen.Condition) IIotGatewayDo
+	Or(conds ...gen.Condition) IIotGatewayDo
+	Select(conds ...field.Expr) IIotGatewayDo
+	Where(conds ...gen.Condition) IIotGatewayDo
+	Order(conds ...field.Expr) IIotGatewayDo
+	Distinct(cols ...field.Expr) IIotGatewayDo
+	Omit(cols ...field.Expr) IIotGatewayDo
+	Join(table schema.Tabler, on ...field.Expr) IIotGatewayDo
+	LeftJoin(table schema.Tabler, on ...field.Expr) IIotGatewayDo
+	RightJoin(table schema.Tabler, on ...field.Expr) IIotGatewayDo
+	Group(cols ...field.Expr) IIotGatewayDo
+	Having(conds ...gen.Condition) IIotGatewayDo
+	Limit(limit int) IIotGatewayDo
+	Offset(offset int) IIotGatewayDo
+	Count() (count int64, err error)
+	Scopes(funcs ...func(gen.Dao) gen.Dao) IIotGatewayDo
+	Unscoped() IIotGatewayDo
+	Create(values ...*model.IotGateway) error
+	CreateInBatches(values []*model.IotGateway, batchSize int) error
+	Save(values ...*model.IotGateway) error
+	First() (*model.IotGateway, error)
+	Take() (*model.IotGateway, error)
+	Last() (*model.IotGateway, error)
+	Find() ([]*model.IotGateway, error)
+	FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.IotGateway, err error)
+	FindInBatches(result *[]*model.IotGateway, batchSize int, fc func(tx gen.Dao, batch int) error) error
+	Pluck(column field.Expr, dest interface{}) error
+	Delete(...*model.IotGateway) (info gen.ResultInfo, err error)
+	Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
+	UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
+	Updates(value interface{}) (info gen.ResultInfo, err error)
+	UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
+	UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
+	UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
+	UpdateFrom(q gen.SubQuery) gen.Dao
+	Attrs(attrs ...field.AssignExpr) IIotGatewayDo
+	Assign(attrs ...field.AssignExpr) IIotGatewayDo
+	Joins(fields ...field.RelationField) IIotGatewayDo
+	Preload(fields ...field.RelationField) IIotGatewayDo
+	FirstOrInit() (*model.IotGateway, error)
+	FirstOrCreate() (*model.IotGateway, error)
+	FindByPage(offset int, limit int) (result []*model.IotGateway, count int64, err error)
+	ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
+	Rows() (*sql.Rows, error)
+	Row() *sql.Row
+	Scan(result interface{}) (err error)
+	Returning(value interface{}, columns ...string) IIotGatewayDo
+	UnderlyingDB() *gorm.DB
+	schema.Tabler
+}
+
+func (i iotGatewayDo) Debug() IIotGatewayDo {
+	return i.withDO(i.DO.Debug())
+}
+
+func (i iotGatewayDo) WithContext(ctx context.Context) IIotGatewayDo {
+	return i.withDO(i.DO.WithContext(ctx))
+}
+
+func (i iotGatewayDo) ReadDB() IIotGatewayDo {
+	return i.Clauses(dbresolver.Read)
+}
+
+func (i iotGatewayDo) WriteDB() IIotGatewayDo {
+	return i.Clauses(dbresolver.Write)
+}
+
+func (i iotGatewayDo) Session(config *gorm.Session) IIotGatewayDo {
+	return i.withDO(i.DO.Session(config))
+}
+
+func (i iotGatewayDo) Clauses(conds ...clause.Expression) IIotGatewayDo {
+	return i.withDO(i.DO.Clauses(conds...))
+}
+
+func (i iotGatewayDo) Returning(value interface{}, columns ...string) IIotGatewayDo {
+	return i.withDO(i.DO.Returning(value, columns...))
+}
+
+func (i iotGatewayDo) Not(conds ...gen.Condition) IIotGatewayDo {
+	return i.withDO(i.DO.Not(conds...))
+}
+
+func (i iotGatewayDo) Or(conds ...gen.Condition) IIotGatewayDo {
+	return i.withDO(i.DO.Or(conds...))
+}
+
+func (i iotGatewayDo) Select(conds ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.Select(conds...))
+}
+
+func (i iotGatewayDo) Where(conds ...gen.Condition) IIotGatewayDo {
+	return i.withDO(i.DO.Where(conds...))
+}
+
+func (i iotGatewayDo) Order(conds ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.Order(conds...))
+}
+
+func (i iotGatewayDo) Distinct(cols ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.Distinct(cols...))
+}
+
+func (i iotGatewayDo) Omit(cols ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.Omit(cols...))
+}
+
+func (i iotGatewayDo) Join(table schema.Tabler, on ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.Join(table, on...))
+}
+
+func (i iotGatewayDo) LeftJoin(table schema.Tabler, on ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.LeftJoin(table, on...))
+}
+
+func (i iotGatewayDo) RightJoin(table schema.Tabler, on ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.RightJoin(table, on...))
+}
+
+func (i iotGatewayDo) Group(cols ...field.Expr) IIotGatewayDo {
+	return i.withDO(i.DO.Group(cols...))
+}
+
+func (i iotGatewayDo) Having(conds ...gen.Condition) IIotGatewayDo {
+	return i.withDO(i.DO.Having(conds...))
+}
+
+func (i iotGatewayDo) Limit(limit int) IIotGatewayDo {
+	return i.withDO(i.DO.Limit(limit))
+}
+
+func (i iotGatewayDo) Offset(offset int) IIotGatewayDo {
+	return i.withDO(i.DO.Offset(offset))
+}
+
+func (i iotGatewayDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IIotGatewayDo {
+	return i.withDO(i.DO.Scopes(funcs...))
+}
+
+func (i iotGatewayDo) Unscoped() IIotGatewayDo {
+	return i.withDO(i.DO.Unscoped())
+}
+
+func (i iotGatewayDo) Create(values ...*model.IotGateway) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return i.DO.Create(values)
+}
+
+func (i iotGatewayDo) CreateInBatches(values []*model.IotGateway, batchSize int) error {
+	return i.DO.CreateInBatches(values, batchSize)
+}
+
+// Save : !!! underlying implementation is different with GORM
+// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
+func (i iotGatewayDo) Save(values ...*model.IotGateway) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return i.DO.Save(values)
+}
+
+func (i iotGatewayDo) First() (*model.IotGateway, error) {
+	if result, err := i.DO.First(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.IotGateway), nil
+	}
+}
+
+func (i iotGatewayDo) Take() (*model.IotGateway, error) {
+	if result, err := i.DO.Take(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.IotGateway), nil
+	}
+}
+
+func (i iotGatewayDo) Last() (*model.IotGateway, error) {
+	if result, err := i.DO.Last(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.IotGateway), nil
+	}
+}
+
+func (i iotGatewayDo) Find() ([]*model.IotGateway, error) {
+	result, err := i.DO.Find()
+	return result.([]*model.IotGateway), err
+}
+
+func (i iotGatewayDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.IotGateway, err error) {
+	buf := make([]*model.IotGateway, 0, batchSize)
+	err = i.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
+		defer func() { results = append(results, buf...) }()
+		return fc(tx, batch)
+	})
+	return results, err
+}
+
+func (i iotGatewayDo) FindInBatches(result *[]*model.IotGateway, batchSize int, fc func(tx gen.Dao, batch int) error) error {
+	return i.DO.FindInBatches(result, batchSize, fc)
+}
+
+func (i iotGatewayDo) Attrs(attrs ...field.AssignExpr) IIotGatewayDo {
+	return i.withDO(i.DO.Attrs(attrs...))
+}
+
+func (i iotGatewayDo) Assign(attrs ...field.AssignExpr) IIotGatewayDo {
+	return i.withDO(i.DO.Assign(attrs...))
+}
+
+func (i iotGatewayDo) Joins(fields ...field.RelationField) IIotGatewayDo {
+	for _, _f := range fields {
+		i = *i.withDO(i.DO.Joins(_f))
+	}
+	return &i
+}
+
+func (i iotGatewayDo) Preload(fields ...field.RelationField) IIotGatewayDo {
+	for _, _f := range fields {
+		i = *i.withDO(i.DO.Preload(_f))
+	}
+	return &i
+}
+
+func (i iotGatewayDo) FirstOrInit() (*model.IotGateway, error) {
+	if result, err := i.DO.FirstOrInit(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.IotGateway), nil
+	}
+}
+
+func (i iotGatewayDo) FirstOrCreate() (*model.IotGateway, error) {
+	if result, err := i.DO.FirstOrCreate(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.IotGateway), nil
+	}
+}
+
+func (i iotGatewayDo) FindByPage(offset int, limit int) (result []*model.IotGateway, count int64, err error) {
+	result, err = i.Offset(offset).Limit(limit).Find()
+	if err != nil {
+		return
+	}
+
+	if size := len(result); 0 < limit && 0 < size && size < limit {
+		count = int64(size + offset)
+		return
+	}
+
+	count, err = i.Offset(-1).Limit(-1).Count()
+	return
+}
+
+func (i iotGatewayDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
+	count, err = i.Count()
+	if err != nil {
+		return
+	}
+
+	err = i.Offset(offset).Limit(limit).Scan(result)
+	return
+}
+
+func (i iotGatewayDo) Scan(result interface{}) (err error) {
+	return i.DO.Scan(result)
+}
+
+func (i iotGatewayDo) Delete(models ...*model.IotGateway) (result gen.ResultInfo, err error) {
+	return i.DO.Delete(models)
+}
+
+func (i *iotGatewayDo) withDO(do gen.Dao) *iotGatewayDo {
+	i.DO = *do.(*gen.DO)
+	return i
+}

+ 75 - 0
service/routes/routes.go

@@ -0,0 +1,75 @@
+package routes
+
+import (
+	// "fwh-toolbox-server/controllers"
+	_ "fwh-toolbox-server/docs"
+	"time"
+
+	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/swagger"
+	"github.com/google/uuid"
+)
+
+const traceIdHeader string = "X-Trace-Id"
+
+type Response[T any] struct {
+	Code      int    `json:"code"`
+	Message   string `json:"message"`
+	Data      T      `json:"data"`
+	TraceID   string `json:"traceId"`
+	Timestamp int64  `json:"timestamp"`
+}
+
+type ResponseNoData struct {
+	Code      int    `json:"code"`
+	Message   string `json:"message"`
+	TraceID   string `json:"traceId"`
+	Timestamp int64  `json:"timestamp"`
+}
+
+func JSONResponse[T any](c *fiber.Ctx, code int, message string, data T) error {
+	traceId := c.Get(traceIdHeader) // 从请求头取
+	if traceId == "" {
+		traceId = uuid.New().String() // 没有就生成
+	}
+
+	// 设置到响应头,让前端也能拿到
+	c.Set(traceIdHeader, traceId)
+
+	resp := Response[T]{
+		Code:      code,
+		Message:   message,
+		Data:      data,
+		TraceID:   traceId,
+		Timestamp: time.Now().UnixMilli(),
+	}
+	return c.JSON(resp)
+}
+
+func JSONResponseNoData(c *fiber.Ctx, code int, message string) error {
+	traceId := c.Get(traceIdHeader) // 从请求头取
+	if traceId == "" {
+		traceId = uuid.New().String() // 没有就生成
+	}
+
+	// 设置到响应头,让前端也能拿到
+	c.Set(traceIdHeader, traceId)
+
+	resp := ResponseNoData{
+		Code:      code,
+		Message:   message,
+		TraceID:   traceId,
+		Timestamp: time.Now().UnixMilli(),
+	}
+	return c.JSON(resp)
+}
+
+func Setup(app *fiber.App) {
+	api := app.Group("/api")
+	// api.Get("/keyAccounts/sunrun/state", controllers.GetUsers)
+	// api.Get("/keyAccounts/sunrun/abnormalList", controllers.GetUsers)
+	// …
+	SitesMonitorSetup(api)
+
+	app.Get("/swagger/*", swagger.HandlerDefault)
+}

+ 184 - 0
service/routes/sitesMonitorRoutes.go

@@ -0,0 +1,184 @@
+/*
+ * Sites Monitor API List
+ * /sitesMonitor/manage/nameList - GET
+ * /sitesMonitor/manage/config/{name} - GET & POST & DELETE
+ * /sitesMonitor/manage/pageConfig/{name} - POST
+ * /sitesMonitor/manage/config - GET & POST
+ * /sitesMonitor/site/infoList - POST 批量获取站点信息表
+ * /sitesMonitor/site/{site}/info - GET 获取单个站点信息
+ * /sitesMonitor/site/{site}/modeInfo - GET 获取站点工作模式信息
+ * /sitesMonitor/site/{site}/powerFlow/{day} - GET 获取单个站点功率流
+ */
+package routes
+
+import (
+	"fmt"
+	"fwh-toolbox-server/controllers"
+	"time"
+
+	"github.com/gofiber/fiber/v2"
+)
+
+// 在sitesMonitorControllers.go中定义
+// type SitesMonitorConfig struct {
+// 	Name        string   `json:"name"`
+// 	Description string   `json:"description"`
+// 	SiteList    []string `json:"siteList"`
+// }
+
+func SitesMonitorSetup(api fiber.Router) {
+	// 获取站点监控管理名称表
+	api.Get("/sitesMonitor/manage/nameList", func(c *fiber.Ctx) error {
+		names, err := controllers.GetSiteMonitorNameList()
+		if err != nil {
+			return JSONResponseNoData(c, 500, "Database read failed. (MongoDb)")
+		}
+		return JSONResponse(c, 0, "success", names)
+	})
+
+	// 获取单个监控配置
+	api.Get("/sitesMonitor/manage/config/:name", func(c *fiber.Ctx) error {
+		name := c.Params("name")
+		fmt.Println(name)
+		configList, err := controllers.GetOneSiteMonitorConfig(name)
+		if err != nil {
+			return JSONResponseNoData(c, 500, "Database read failed. (MongoDb)")
+		} else if len(configList) != 1 {
+			return JSONResponseNoData(c, 204, "User not found")
+		}
+		return JSONResponse(c, 0, "success", configList[0])
+	})
+
+	// 更新一个监控配置
+	api.Post("/sitesMonitor/manage/config/:name", func(c *fiber.Ctx) error {
+		name := c.Params("name")
+		var body controllers.SitesMonitorConfig
+		if err := c.BodyParser(&body); err != nil {
+			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+				"code":    400,
+				"message": "Invalid request body",
+			})
+		}
+		fmt.Println(body.Name, body.Description, body.SiteList)
+		err := controllers.UpdateSiteMonitorConfig(name, body)
+		if err != nil {
+			return JSONResponseNoData(c, 500, "Database write failed. (MongoDb)")
+		}
+		return JSONResponse(c, 0, "success", body)
+	})
+
+	// 更新一个监控页面配置
+	api.Post("/sitesMonitor/manage/pageConfig/:name", func(c *fiber.Ctx) error {
+		name := c.Params("name")
+		var body controllers.MonitorPageConfig
+		if err := c.BodyParser(&body); err != nil {
+			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+				"code":    400,
+				"message": "Invalid request body",
+			})
+		}
+		err := controllers.UpdateSiteMonitorPageConfig(name, body)
+		if err != nil {
+			return JSONResponseNoData(c, 500, "Database write failed. (MongoDb)")
+		}
+		return JSONResponse(c, 0, "success", body)
+	})
+
+	// 删除一个监控
+	api.Delete("/sitesMonitor/manage/config/:name", func(c *fiber.Ctx) error {
+		name := c.Params("name")
+		fmt.Println(name)
+		err := controllers.DeleteSiteMonitorConfig(name)
+		if err != nil {
+			return JSONResponseNoData(c, 500, "Database delete failed. (MongoDb)")
+		}
+		return JSONResponseNoData(c, 0, "success")
+	})
+
+	// 获取所有监控配置
+	api.Get("/sitesMonitor/manage/config", func(c *fiber.Ctx) error {
+		configList, err := controllers.GetSiteMonitorConfig()
+		if err != nil {
+			return JSONResponseNoData(c, 500, "Database read failed. (MongoDb)")
+		}
+		return JSONResponse(c, 0, "success", configList)
+	})
+
+	// 创建一个监控配置
+	api.Post("/sitesMonitor/manage/config", func(c *fiber.Ctx) error {
+		var body controllers.SitesMonitorConfig
+		if err := c.BodyParser(&body); err != nil {
+			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+				"code":    400,
+				"message": "Invalid request body",
+			})
+		}
+		fmt.Println(body.Name, body.Description, body.SiteList)
+		err := controllers.InsertSiteMonitorConfig(body)
+		if err != nil {
+			return JSONResponseNoData(c, 500, "Database write failed. (MongoDb)")
+		}
+		return JSONResponse(c, 0, "success", body)
+	})
+
+	// 批量获取站点信息表
+	api.Post("/sitesMonitor/site/infoList", func(c *fiber.Ctx) error {
+		var body controllers.SiteInfoPara
+		if err := c.BodyParser(&body); err != nil {
+			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+				"code":    400,
+				"message": "Invalid request body",
+			})
+		}
+		infoList, err := controllers.QuerySiteInfoList(body)
+		if err != nil {
+			fmt.Println("发生错误:", err)
+			return JSONResponseNoData(c, 500, "Database write failed. (MongoDb)")
+		}
+		return JSONResponse(c, 0, "success", infoList)
+	})
+
+	// 获取单个站点信息
+	api.Get("/sitesMonitor/site/:site/info", func(c *fiber.Ctx) error {
+		site := c.Params("site")
+		siteInfo, err := controllers.QuerySiteInfoList(controllers.SiteInfoPara{
+			SiteList: []string{site},
+			PageSize: 10,
+			PageNum:  1,
+		})
+		if err != nil {
+			fmt.Println("发生错误:", err)
+			return JSONResponseNoData(c, 500, "fwh_pro query failed.")
+		}
+		return JSONResponse(c, 0, "success", siteInfo)
+	})
+
+	// 获取单个站点工作模式信息
+	api.Get("/sitesMonitor/site/:site/modeInfo", func(c *fiber.Ctx) error {
+		site := c.Params("site")
+		siteModeInfo, err := controllers.QuerySiteModeInfo(site)
+		if err != nil {
+			fmt.Println("发生错误:", err)
+			return JSONResponseNoData(c, 500, "fwh_pro query failed.")
+		}
+		return JSONResponse(c, 0, "success", siteModeInfo)
+	})
+
+	// 获取单个站点功率流
+	api.Get("/sitesMonitor/site/:site/powerFlow/:day", func(c *fiber.Ctx) error {
+		site := c.Params("site")
+		day := c.Params("day")
+		dayTime, err := time.Parse(time.DateOnly, day)
+		if err != nil {
+			fmt.Println("发生错误:", err)
+			return JSONResponseNoData(c, 500, "day parament error.")
+		}
+
+		sitePowerFlow, err := controllers.QuerySitePowerFlow(site, dayTime)
+		if err != nil {
+			fmt.Println("发生错误:", err)
+			return JSONResponseNoData(c, 500, "fwh_pro query failed.")
+		}
+		return JSONResponse(c, 0, "success", sitePowerFlow)
+	})
+}

+ 16 - 0
service/scheduler/scheduler.go

@@ -0,0 +1,16 @@
+package scheduler
+
+import (
+  "github.com/robfig/cron/v3"
+  "log"
+)
+
+func Init() *cron.Cron {
+  c := cron.New()
+  c.AddFunc("@every 1h", func() {
+    log.Println("每小时任务执行")
+    // do job
+  })
+  c.Start()
+  return c
+}

+ 55 - 0
src/App.vue

@@ -0,0 +1,55 @@
+<script setup lang="ts">
+import {
+  NConfigProvider,
+  NModalProvider,
+  NDialogProvider,
+  NNotificationProvider,
+  NMessageProvider,
+  NWatermark,
+  NGlobalStyle,
+  NEl,
+} from 'naive-ui'
+import { RouterView } from 'vue-router'
+
+import Noise from '@/components/Noise.vue'
+import { getConfigProviderProps } from '@/composable/useDiscreteApi'
+import { useConfigureStore } from '@/stores/configure'
+
+const configureStore = useConfigureStore()
+const configProviderProps = getConfigProviderProps()
+
+defineOptions({
+  // 禁止参数透传
+  inheritAttrs: false
+})
+
+import { ref, onMounted } from 'vue';
+
+const configProvider = ref(null)
+
+onMounted(() => {
+  if (configProvider.value) {
+    console.log(configProvider.value);  // 直接获取当前组件实例的 attrs
+  }
+})
+</script>
+
+<template>
+  <NConfigProvider ref="configProvider" v-bind="configProviderProps">
+    <NGlobalStyle />
+    <NEl>
+      <NModalProvider>
+        <NNotificationProvider placement="bottom-right">
+          <NMessageProvider>
+            <NDialogProvider>
+              <RouterView />
+              <NWatermark v-if="configureStore.configure.showWatermark" fullscreen
+                v-bind="configureStore.configure.watermarkOptions" />
+              <Noise v-if="configureStore.configure.showNoise" />
+            </NDialogProvider>
+          </NMessageProvider>
+        </NNotificationProvider>
+      </NModalProvider>
+    </NEl>
+  </NConfigProvider>
+</template>

+ 30 - 0
src/assets/base.css

@@ -0,0 +1,30 @@
+@import 'tailwindcss' source(none);
+
+@source not "../../src/**/*.test.ts";
+@source not "../../src/**/*.d.ts";
+@source not inline("{filter,transition}");
+
+@source "../../src/**/*.vue";
+@source "../../src/**/*.ts";
+@source "../../src/**/*.tsx";
+
+@plugin "@iconify/tailwind4" {
+  prefixes: ph;
+  prefix: 'iconify';
+  scale: 1.2;
+}
+
+@custom-variant dark (&:where(.dark, .dark *));
+
+@config "../../tailwind.config.ts";
+
+@theme inline {
+  --color-primary: var(--primary-color);
+  --color-naive-border: var(--border-color);
+  --color-naive-card: var(--card-color);
+
+  --ease-naive-bezier: var(--cubic-bezier-ease-in-out);
+  --ease-naive-bezier-in: var(--cubic-bezier-ease-in);
+  --ease-naive-bezier-out: var(--cubic-bezier-ease-out);
+  --ease-naive-bezier-in-out: var(--cubic-bezier-ease-in-out);
+}

+ 13 - 0
src/assets/main.css

@@ -0,0 +1,13 @@
+/* @import './base.css'; */
+
+@layer base {
+  body {
+    @apply overflow-hidden tracking-wide;
+  }
+}
+
+@theme inline {
+  --color-primary: var(--primary-color);
+  --color-naive-border: var(--border-color);
+  --color-naive-card: var(--card-color);
+}

BIN
src/assets/noise.png


BIN
src/assets/texture.png


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
src/assets/topography.svg


+ 64 - 0
src/components/Avatar.vue

@@ -0,0 +1,64 @@
+<template>
+  <svg
+    xmlns="http://www.w3.org/2000/svg"
+    viewBox="0 0 640 640"
+  >
+    <defs>
+      <clipPath id="a">
+        <circle
+          cx="320"
+          cy="320"
+          r="320"
+          fill="none"
+          stroke="#707070"
+          transform="translate(626 151)"
+        />
+      </clipPath>
+    </defs>
+    <g transform="translate(-626 -151)">
+      <circle
+        cx="320"
+        cy="320"
+        r="320"
+        fill="#fff"
+        transform="translate(626 151)"
+      />
+      <g clip-path="url(#a)">
+        <path
+          fill="#ffb9b9"
+          d="M900.5 416s13.9 104.4-7.4 116.1 126.8 12.8 126.8 12.8-33-90.6-18.1-118.3Z"
+        />
+        <circle
+          cx="73.5"
+          cy="73.5"
+          r="73.5"
+          fill="#ffb9b9"
+          transform="translate(881.9 324.4)"
+        />
+        <path
+          fill="currentColor"
+          d="m1128 545.4-12.3 56.1-26 118.7-.8 6.5-9 75.3-6 50-4 33.5c-24.3 12.6-42 21.2-42 21.2s-1.5-7.6-4-17.3a372.9 372.9 0 0 1-75.6 15.3c9.8 10.9-141.1-266.1-146.9-303.2a3614.4 3614.4 0 0 1-9-61.4l112.4-49.7c8 18.6 45.3 24.1 45.3 24.1 30.9-2 55.1-18.4 55.1-18.4Z"
+        />
+        <path
+          fill="#090814"
+          d="m1017.8 328.4 13.7-5.5s-28.7-31.7-68.7-29l11.2-12.3s-27.5-11-52.5 17.9c-13 15.2-28.3 33-37.8 53.1H869l6.1 13.6-21.5 13.5 22.1-2.4a75.6 75.6 0 0 0-.6 22.4 29 29 0 0 0 10.6 19s17.1-35.3 17.1-40.8v13.8s13.7-12.4 13.7-20.7l7.5 9.7 3.8-15.2 46.2 15.2-7.5-12.4 28.8 4.1-11.3-15.1s32.5 17.9 33.8 33 10.8 29.5 10.8 29.5 25.4-70.8-10.8-91.4Z"
+        />
+        <path
+          fill="currentColor"
+          d="M1208 803.3c-7.5 8.2-26 20.4-48.5 33.6a2319.9 2319.9 0 0 1-89.5 48.6c-24.4 12.6-42.1 21.2-42.1 21.2s-1.5-7.6-4-17.3c-3.3-13-8.2-30-13.3-37.4l-.6-.8c-1.5-2-3-3-4.5-3L1080 802l32.2-20-23.2-55.3-29-69.4 17.5-55.8 17.6-56h33s11 23.8 25 57.1l6.5 15.5c28.7 70 64.6 167.8 48.5 185.2ZM875.5 851.3a47.8 47.8 0 0 0-8.1.7c-23 4-29.8 24.7-31.7 38.7a71.8 71.8 0 0 0-.7 12.8L814.7 888l-7.4-5.7a134.9 134.9 0 0 1-47.5-30.2 238.5 238.5 0 0 1-32-38 334.1 334.1 0 0 1-24.6-42.3 31.2 31.2 0 0 1-.5-26.5l25.2-56 36.7-82q.4-2.9 1-5.7c7.2-41.8 26.8-60.3 26.8-60.3h14.9l10 60.3 12.4 75L821 705l-20.1 65.4 19.2 20.9Z"
+        />
+        <path
+          fill="#090814"
+          d="M1147.2 629.7H980.8v-3.4h-75.5v3.4H738.2a11.3 11.3 0 0 0-11.3 11.3v228a11.3 11.3 0 0 0 11.3 11.2h409a11.3 11.3 0 0 0 11.3-11.3V641a11.3 11.3 0 0 0-11.3-11.3Z"
+        />
+        <circle
+          cx="25"
+          cy="25"
+          r="25"
+          fill="#fff"
+          transform="translate(921.4 684.3)"
+        />
+      </g>
+    </g>
+  </svg>
+</template>

+ 112 - 0
src/components/ButtonAnimation.vue

@@ -0,0 +1,112 @@
+<script setup lang="ts">
+import { NButton } from 'naive-ui'
+import { ref } from 'vue'
+
+import type { ButtonProps } from 'naive-ui'
+
+type Animation = 'beat' | 'rotate' | 'shake'
+
+interface ButtonAnimationProps extends /* @vue-ignore */ ButtonProps {
+  duration?: number
+  animation?: Animation | boolean
+}
+
+const { duration = 600, animation = 'beat' } = defineProps<ButtonAnimationProps>()
+
+const isAnimating = ref(false)
+
+const onButtonClicked = () => {
+  if (isAnimating.value) return
+
+  isAnimating.value = true
+
+  setTimeout(() => {
+    isAnimating.value = false
+  }, duration)
+}
+</script>
+
+<template>
+  <NButton
+    quaternary
+    circle
+    v-bind="$attrs"
+    @click.stop="onButtonClicked"
+  >
+    <template #icon>
+      <div
+        class="grid place-items-center"
+        :class="{
+          'animate-beat': isAnimating && animation === 'beat',
+          'animate-rotate': isAnimating && animation === 'rotate',
+          'animate-shake': isAnimating && animation === 'shake',
+        }"
+        :style="{
+          '--duration': `${duration}ms`,
+        }"
+      >
+        <slot />
+      </div>
+    </template>
+  </NButton>
+</template>
+<style scoped>
+.animate-beat {
+  animation: beat var(--duration) ease-in-out;
+}
+
+@keyframes beat {
+  0%,
+  50%,
+  100% {
+    scale: 1;
+  }
+  25% {
+    scale: 1.1;
+  }
+
+  75% {
+    scale: 1.2;
+  }
+}
+
+.animate-rotate {
+  animation: rotate var(--duration) ease-in-out;
+}
+
+@keyframes rotate {
+  0% {
+    rotate: 0;
+  }
+  100% {
+    rotate: 360deg;
+  }
+}
+
+.animate-shake {
+  animation: shake var(--duration) ease-in-out;
+}
+
+@keyframes shake {
+  10%,
+  90% {
+    translate: -1px;
+  }
+
+  20%,
+  80% {
+    translate: 2px;
+  }
+
+  30%,
+  50%,
+  70% {
+    translate: -3px;
+  }
+
+  40%,
+  60% {
+    translate: 3px;
+  }
+}
+</style>

+ 41 - 0
src/components/EmptyPlaceholder.vue

@@ -0,0 +1,41 @@
+<script setup lang="ts">
+import { NEmpty } from 'naive-ui'
+
+import type { EmptyProps, EmptySlots } from 'naive-ui'
+import type { VNode } from 'vue'
+
+export interface EmptyPlaceholderProps extends /* @vue-ignore */ EmptyProps {
+  show?: boolean
+}
+
+export interface EmptyPlaceholderSlots extends EmptySlots {
+  content?: () => VNode[]
+}
+
+defineOptions({
+  inheritAttrs: false,
+})
+
+const props = defineProps<EmptyPlaceholderProps>()
+
+const slots = defineSlots<EmptyPlaceholderSlots>()
+
+function getEmptySlots() {
+  return (Object.keys(slots) as (keyof EmptyPlaceholderSlots)[]).filter(
+    (name) => name !== 'content',
+  )
+}
+</script>
+<template>
+  <Transition enter-active-class="transition-[opacity,scale]-300 transition-ease-in-out"
+    enter-from-class="scale-50 opacity-0">
+    <div v-if="props.show" class="absolute inset-0 grid size-full place-items-center">
+      <NEmpty v-bind="$attrs">
+        <template v-for="slot of getEmptySlots()" :key="slot" #[slot]>
+          <slot :name="slot" />
+        </template>
+      </NEmpty>
+    </div>
+  </Transition>
+  <slot name="content" />
+</template>

+ 36 - 0
src/components/HintHelp.vue

@@ -0,0 +1,36 @@
+<script setup lang="ts">
+import { isFunction } from 'es-toolkit'
+import { NButton, NPopover } from 'naive-ui'
+import { isVNode } from 'vue'
+
+import type { ButtonProps, PopoverProps } from 'naive-ui'
+import type { AllowedComponentProps, HTMLAttributes, SVGAttributes, VNodeChild } from 'vue'
+
+interface HintHelpProps {
+  buttonProps?: AllowedComponentProps & ButtonProps
+  content?: (() => VNodeChild) | string
+  iconProps?: SVGAttributes
+  label?: string
+  labelProps?: HTMLAttributes
+  popoverProps?: PopoverProps
+}
+
+const { label, popoverProps, buttonProps, iconProps, content } = defineProps<HintHelpProps>()
+</script>
+<template>
+  <div class="flex items-center gap-x-0.5">
+    <span>{{ label }}</span>
+    <NPopover v-bind="popoverProps">
+      <template #trigger>
+        <NButton quaternary circle size="small" v-bind="buttonProps">
+          <template #icon>
+            <span class="i-ph:question" v-bind="iconProps" />
+          </template>
+        </NButton>
+      </template>
+
+      <component v-if="isFunction(content) && isVNode(content())" :is="content" />
+      <span v-else>{{ content }}</span>
+    </NPopover>
+  </div>
+</template>

+ 15 - 0
src/components/Noise.vue

@@ -0,0 +1,15 @@
+<script setup lang="ts">
+import noisePng from '@/assets/noise.png'
+import { useConfigureStore } from '@/stores/configure'
+
+const configureStore = useConfigureStore()
+</script>
+<template>
+  <div
+    class="pointer-events-none fixed inset-0 z-[99998] size-full"
+    :style="{
+      backgroundImage: `url(${noisePng})`,
+      opacity: configureStore.configure.noiseOpacity,
+    }"
+  />
+</template>

+ 80 - 0
src/components/__tests__/comp.test.ts

@@ -0,0 +1,80 @@
+import { mount } from '@vue/test-utils'
+import { describe, it, expect } from 'vitest'
+
+import { ButtonAnimation, EmptyPlaceholder } from '../index'
+
+describe('ButtonAnimation Component', () => {
+  it('render component', () => {
+    const wrapper = mount(ButtonAnimation, {
+      slots: {
+        default: '<div>context</div>',
+      },
+    })
+
+    expect(wrapper.exists()).toBe(true)
+    expect(wrapper.find('.grid.place-items-center').exists()).toBe(true)
+  })
+
+  it('click to trigger animation', async () => {
+    const wrapper = mount(ButtonAnimation, {
+      props: {
+        animation: 'beat',
+        duration: 100,
+      },
+      slots: {
+        default: '<div>context</div>',
+      },
+    })
+
+    expect(wrapper.find('.animate-beat').exists()).toBe(false)
+
+    await wrapper.find('button').trigger('click')
+
+    expect(wrapper.find('.animate-beat').exists()).toBe(true)
+
+    await new Promise((resolve) => setTimeout(resolve, 150))
+
+    expect(wrapper.find('.animate-beat').exists()).toBe(false)
+  })
+
+  it('render different animation effects according to props', async () => {
+    const wrapper = mount(ButtonAnimation, {
+      props: {
+        animation: 'shake',
+        duration: 100,
+      },
+    })
+
+    await wrapper.find('button').trigger('click')
+    expect(wrapper.find('.animate-shake').exists()).toBe(true)
+    expect(wrapper.find('.animate-pulse').exists()).toBe(false)
+  })
+})
+
+describe('EmptyPlaceholder Component', () => {
+  it('render component', () => {
+    const wrapper = mount(EmptyPlaceholder, {
+      props: {
+        show: true,
+      },
+    })
+
+    expect(wrapper.exists()).toBe(true)
+    expect(wrapper.find('.n-empty').exists()).toBe(true)
+  })
+
+  it('add content slot', () => {
+    const wrapper = mount(EmptyPlaceholder, {
+      props: {
+        show: true,
+      },
+      slots: {
+        content: '<div class="content">context</div>',
+      },
+    })
+
+    expect(wrapper.exists()).toBe(true)
+    expect(wrapper.find('.n-empty').exists()).toBe(true)
+    expect(wrapper.find('.content').exists()).toBe(true)
+  })
+})

+ 9 - 0
src/components/index.ts

@@ -0,0 +1,9 @@
+import ButtonAnimation from './ButtonAnimation.vue'
+import EmptyPlaceholder from './EmptyPlaceholder.vue'
+import HintHelp from './HintHelp.vue'
+
+export { EmptyPlaceholder, HintHelp, ButtonAnimation }
+
+export * from './ButtonAnimation.vue'
+export * from './EmptyPlaceholder.vue'
+export * from './HintHelp.vue'

+ 56 - 0
src/composable/comp/useComponentModifier.ts

@@ -0,0 +1,56 @@
+import type { ModalProps, PopconfirmProps } from 'naive-ui'
+
+export function useComponentModifier() {
+  function getModalModifier(props?: ModalProps): ModalProps {
+    const negativeButtonProps: ModalProps['negativeButtonProps'] = {
+      ghost: false,
+      secondary: true,
+      type: 'tertiary',
+    }
+
+    const positiveButtonProps: ModalProps['positiveButtonProps'] = {
+      ghost: false,
+      secondary: true,
+    }
+
+    const themeOverrides: ModalProps['themeOverrides'] = {
+      peers: {
+        Dialog: {
+          iconSize: '26px',
+          titleFontSize: '16px',
+        },
+      },
+    }
+
+    return {
+      negativeButtonProps,
+      positiveButtonProps,
+      themeOverrides,
+      ...props,
+    }
+  }
+
+  function getPopconfirmModifier(props?: PopconfirmProps): PopconfirmProps {
+    const negativeButtonProps: PopconfirmProps['negativeButtonProps'] = {
+      ghost: false,
+      secondary: true,
+      size: 'small',
+    }
+
+    const positiveButtonProps: PopconfirmProps['positiveButtonProps'] = {
+      ghost: false,
+      type: 'info',
+      size: 'small',
+    }
+
+    return {
+      negativeButtonProps,
+      positiveButtonProps,
+      ...props,
+    }
+  }
+  return {
+    getModalModifier,
+    getPopconfirmModifier,
+  }
+}

+ 72 - 0
src/composable/comp/useComponentThemeOverrides.ts

@@ -0,0 +1,72 @@
+import { computed } from 'vue'
+
+import thc from '@/utils/themeColor'
+
+import { usePersonalization } from '../usePersonalization'
+
+import type { GlobalThemeOverrides } from 'naive-ui'
+
+export function useComponentThemeOverrides() {
+  const { isDark } = usePersonalization()
+
+  const scrollbarInModal = computed<GlobalThemeOverrides['Scrollbar']>(() =>
+    isDark.value
+      ? {
+          color: thc.neutral[750],
+          colorHover: thc.neutral[700],
+        }
+      : {
+          color: thc.neutral[200],
+          colorHover: thc.neutral[250],
+        },
+  )
+
+  const scrollbarInMainLayout = computed<GlobalThemeOverrides['Scrollbar']>(() =>
+    isDark.value
+      ? {
+          color: thc.neutral[800],
+          colorHover: thc.neutral[750],
+        }
+      : {
+          color: thc.neutral[350],
+          colorHover: thc.neutral[400],
+        },
+  )
+
+  const selectInPopover = computed<GlobalThemeOverrides['Select']>(() =>
+    isDark.value
+      ? {
+          peers: {
+            InternalSelectMenu: {
+              color: thc.neutral[700],
+              optionColorPending: thc.neutral[600],
+            },
+          },
+        }
+      : {
+          peers: {
+            InternalSelectMenu: {
+              color: '#fff',
+              optionColorPending: thc.neutral[150],
+            },
+          },
+        },
+  )
+
+  const datePickerInPopover = computed<GlobalThemeOverrides['DatePicker']>(() =>
+    isDark.value
+      ? {
+          panelColor: thc.neutral[700],
+        }
+      : {
+          panelColor: '#fff',
+        },
+  )
+
+  return {
+    scrollbarInModal,
+    scrollbarInMainLayout,
+    selectInPopover,
+    datePickerInPopover,
+  }
+}

+ 110 - 0
src/composable/comp/useDataTable.ts

@@ -0,0 +1,110 @@
+import { isString, debounce } from 'es-toolkit'
+import { isElement } from 'es-toolkit/compat'
+import { isRef, nextTick, onMounted, reactive, ref, onBeforeUnmount } from 'vue'
+
+import type { ComponentPublicInstance, Ref } from 'vue'
+
+type MaybeHTMLElement = HTMLElement | Ref<HTMLElement | null> | string | null
+
+interface DataTableOptions {
+  parentContainer?: MaybeHTMLElement
+  parentWrap?: MaybeHTMLElement
+}
+
+type ElementKey = 'container' | 'dataTable' | 'table' | 'tableThead' | 'wrap'
+
+export function useDataTable<T extends ComponentPublicInstance>(
+  target: Ref<T | null>,
+  options: DataTableOptions = {},
+) {
+  const { parentContainer = '.main-container', parentWrap = '.main-wrap' } = options
+
+  let resizeObserver: ResizeObserver | null = null
+
+  const maxHeight = ref<number | undefined>(0)
+
+  const elementMap = reactive<Record<ElementKey, HTMLElement | null>>({
+    dataTable: null,
+    table: null,
+    tableThead: null,
+    container: null,
+    wrap: null,
+  })
+
+  function updateMaxHeight() {
+    const { container, wrap, tableThead, table } = elementMap
+
+    if (!container || !wrap || !tableThead || !table) {
+      return
+    }
+
+    const containerHeight = container.offsetHeight
+    const wrapHeight = wrap.offsetHeight
+    const theadHeight = tableThead.offsetHeight
+    const tableHeight = table.offsetHeight
+
+    maxHeight.value =
+      (containerHeight < wrapHeight
+        ? tableHeight - theadHeight - (wrapHeight - containerHeight)
+        : containerHeight - wrapHeight - theadHeight + tableHeight) - 1
+  }
+
+  const debounceUpdateMaxHeight = debounce(() => {
+    updateMaxHeight()
+  }, 300)
+
+  function getParentElement(element: MaybeHTMLElement) {
+    if (isString(element)) return elementMap.dataTable?.closest<HTMLElement>(element) ?? null
+    if (isRef(element)) return element.value
+    if (isElement(element)) return element
+    return null
+  }
+
+  function initTableResizeObserver() {
+    if (!target.value) {
+      maxHeight.value = undefined
+      return
+    }
+
+    elementMap.dataTable = (target.value?.$el as HTMLElement) ?? null
+    elementMap.table = elementMap.dataTable?.querySelector('.n-data-table-base-table') ?? null
+    elementMap.tableThead = elementMap.table?.querySelector('.n-data-table-thead') ?? null
+
+    nextTick(() => {
+      elementMap.container = getParentElement(parentContainer)
+      elementMap.wrap = getParentElement(parentWrap)
+
+      const missingKeys = Object.entries(elementMap)
+        .filter((value) => value[1] == null)
+        .map(([key]) => key)
+
+      if (missingKeys.length) {
+        throw new Error(`Missing or null elements: ${missingKeys.join(', ')}`)
+      }
+
+      resizeObserver = new ResizeObserver((entries) => {
+        for (const entry of entries) {
+          const { height } = entry.contentRect
+          if (height) debounceUpdateMaxHeight()
+        }
+      })
+
+      if (elementMap.container) {
+        resizeObserver.observe(elementMap.container)
+      }
+    })
+  }
+
+  onMounted(() => {
+    initTableResizeObserver()
+  })
+
+  onBeforeUnmount(() => {
+    resizeObserver?.disconnect()
+  })
+
+  return {
+    maxHeight,
+    updateMaxHeight,
+  }
+}

+ 36 - 0
src/composable/useDiscreteApi.ts

@@ -0,0 +1,36 @@
+import { zhCN, dateZhCN, createDiscreteApi } from 'naive-ui'
+import { computed, h } from 'vue'
+
+import { useTheme } from './useTheme'
+
+import type { ConfigProviderProps } from 'naive-ui'
+
+export function getConfigProviderProps() {
+  const { theme, themeOverrides } = useTheme()
+
+  const configProviderProps = computed<ConfigProviderProps>(() => ({
+    locale: zhCN,
+    dateLocale: dateZhCN,
+    theme: theme.value,
+    themeOverrides: themeOverrides.value,
+    icons: {
+      info: () => h('span', { class: 'i-ph:info size-full' }),
+      success: () => h('span', { class: 'i-ph:seal-check size-full' }),
+      warning: () => h('span', { class: 'i-ph:warning size-full' }),
+      error: () => h('span', { class: 'i-ph:x-square size-full' }),
+    },
+  }))
+
+  return configProviderProps
+}
+
+export function useDiscreteApi() {
+  const configProviderProps = getConfigProviderProps()
+
+  return createDiscreteApi(['message', 'dialog', 'notification', 'loadingBar', 'modal'], {
+    configProviderProps,
+    notificationProviderProps: {
+      placement: 'bottom-left',
+    },
+  })
+}

+ 88 - 0
src/composable/usePersonalization.ts

@@ -0,0 +1,88 @@
+import { ref } from 'vue'
+
+export type Theme = 'dark' | 'light' | 'system'
+
+interface PersonalizationOptions {
+  storageColorKey: string
+  defaultColor: string
+  defaultTheme: Theme
+  storageThemeKey: string
+}
+
+let personalizationInstance: ReturnType<typeof setupPersonalization> | null = null
+
+function setupPersonalization(options: PersonalizationOptions) {
+  const { storageColorKey, defaultColor, defaultTheme, storageThemeKey } = options
+
+  const rootElement = window.document.documentElement
+  const prefersDarkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
+
+  const theme = ref<Theme>((localStorage.getItem(storageThemeKey) as Theme) || defaultTheme)
+  const color = ref(localStorage.getItem(storageColorKey) || defaultColor)
+  const isDark = ref(false)
+
+  function setTheme(value: Theme) {
+    theme.value = value
+
+    if (value === 'system') {
+      localStorage.removeItem(storageThemeKey)
+    } else if (['dark', 'light'].includes(value)) {
+      localStorage.setItem(storageThemeKey, theme.value)
+    }
+
+    toggleDocumentElementClass()
+  }
+
+  function setColor(value: string) {
+    color.value = value
+    localStorage.setItem(storageColorKey, color.value)
+  }
+
+  function toggleDocumentElementClass() {
+    const isDarkTheme =
+      localStorage.getItem(storageThemeKey) === 'dark' ||
+      (!localStorage.getItem(storageThemeKey) && prefersDarkMediaQuery.matches)
+
+    isDark.value = isDarkTheme
+
+    rootElement.classList.toggle('dark', isDarkTheme)
+  }
+
+  function updateTheme() {
+    const storageTheme = localStorage.getItem(storageThemeKey) as Theme
+    if (['dark', 'light'].includes(storageTheme)) {
+      setTheme(storageTheme)
+    } else {
+      setTheme('system')
+    }
+  }
+
+  function destroy() {
+    prefersDarkMediaQuery.removeEventListener('change', toggleDocumentElementClass)
+    window.removeEventListener('storage', updateTheme)
+  }
+
+  toggleDocumentElementClass()
+  prefersDarkMediaQuery.addEventListener('change', toggleDocumentElementClass)
+  window.addEventListener('storage', updateTheme)
+
+  return {
+    color,
+    isDark,
+    theme,
+    setColor,
+    setTheme,
+    destroy,
+  }
+}
+
+export const usePersonalization = () => {
+  personalizationInstance ??= setupPersonalization({
+      storageColorKey: 'color',
+      defaultColor: '#8e51ff',
+      defaultTheme: 'system',
+      storageThemeKey: 'theme',
+    });
+
+  return personalizationInstance
+}

+ 53 - 0
src/composable/useResettable.ts

@@ -0,0 +1,53 @@
+import { reactive, ref } from 'vue'
+
+import type { Reactive, Ref } from 'vue'
+
+function clone<T>(value: T): T {
+  if (value === null || typeof value !== 'object') return value
+
+  try {
+    return structuredClone(value)
+  } catch (error) {
+    console.warn('structuredClone failed, using JSON fallback:', error)
+    return JSON.parse(JSON.stringify(value))
+  }
+}
+
+export function useResettableRef<S>(
+  initialState: S,
+): [Ref<S>, () => void, (initialState: S) => void] {
+  let initialValue = clone(initialState)
+  const state = ref<S>(clone(initialState)) as Ref<S>
+
+  const reset = () => {
+    state.value = clone(initialValue)
+  }
+
+  const updateInitialState = (initialState: S) => {
+    initialValue = clone(initialState)
+  }
+
+  return [state, reset, updateInitialState]
+}
+
+export function useResettableReactive<S extends object>(
+  initialState: S,
+): [Reactive<S>, (value: Partial<S>) => void, () => void, (initialState: S) => void] {
+  let initialValue = clone(initialState)
+  const state = reactive<S>(clone(initialState)) as Reactive<S>
+
+  const setState = (value: Partial<S>) => {
+    Object.assign(state, value)
+  }
+
+  const reset = () => {
+    Object.keys(state).forEach((key) => delete state[key as keyof typeof state])
+    Object.assign(state, clone(initialValue))
+  }
+
+  const updateInitialState = (initialState: S) => {
+    initialValue = clone(initialState)
+  }
+
+  return [state, setState, reset, updateInitialState]
+}

+ 57 - 0
src/composable/useTheme.ts

@@ -0,0 +1,57 @@
+import { merge } from 'es-toolkit'
+import { darkTheme, lightTheme } from 'naive-ui'
+import { computed } from 'vue'
+
+import { commonThemeOverrides } from '@/theme/common'
+import { baseDarkThemeOverrides } from '@/theme/dark'
+import { baseLightThemeOverrides } from '@/theme/light'
+
+import { usePersonalization } from './usePersonalization'
+
+export function useTheme() {
+  const { color, isDark } = usePersonalization()
+  // console.log({ lightTheme, darkTheme })
+  // console.log({
+  //   lightThemeOverrides: merge(
+  //     commonThemeOverrides(color.value),
+  //     baseLightThemeOverrides(color.value),
+  //   ),
+  //   darkThemeOverrides: merge(
+  //     commonThemeOverrides(color.value),
+  //     baseDarkThemeOverrides(color.value),
+  //   ),
+  // })
+
+  // console.log({
+  //   lightThemeSelf: {
+  //     Popover: lightTheme.Popover.self?.(lightTheme.Popover.common ?? lightTheme.common),
+  //     Dropdown: lightTheme.Dropdown.self?.(lightTheme.Dropdown.common ?? lightTheme.common),
+  //     Tooltip: lightTheme.Tooltip.self?.(lightTheme.Tooltip.common ?? lightTheme.common),
+  //   },
+  // })
+
+  const getLightThemeOverrides = (primaryColor = color.value) => {
+    return merge(commonThemeOverrides(primaryColor), baseLightThemeOverrides(primaryColor))
+  }
+
+  const getDarkThemeOverrides = (primaryColor = color.value) => {
+    return merge(commonThemeOverrides(primaryColor), baseDarkThemeOverrides(primaryColor))
+  }
+
+  const themeOverrides = computed(() => {
+    return isDark.value ? getDarkThemeOverrides(color.value) : getLightThemeOverrides(color.value)
+  })
+
+  const theme = computed(() => {
+    return isDark.value ? darkTheme : lightTheme
+  })
+
+  return {
+    color,
+    isDark,
+    theme,
+    themeOverrides,
+    getLightThemeOverrides,
+    getDarkThemeOverrides,
+  }
+}

+ 8 - 0
src/injection/index.ts

@@ -0,0 +1,8 @@
+import type { TabsInjection, MenuInjection } from './interface'
+import type { InjectionKey } from 'vue'
+
+export const tabsInjectionKey = Symbol() as InjectionKey<TabsInjection>
+
+export const menuInjectionKey = Symbol() as InjectionKey<MenuInjection>
+
+export * from './interface'

+ 13 - 0
src/injection/interface.ts

@@ -0,0 +1,13 @@
+import type { Reactive, Ref } from 'vue'
+
+export interface TabsInjection {
+  doRefresh: (value: boolean) => void
+  shouldRefresh: Ref<boolean>
+}
+
+export interface MenuInjection {
+  collapse: Reactive<{
+    width: number
+    maxWidth: number
+  }>
+}

+ 44 - 0
src/layout/aside/component/Menu.vue

@@ -0,0 +1,44 @@
+<script setup lang="ts">
+import { NMenu, NScrollbar } from 'naive-ui'
+import { inject, ref, useTemplateRef, watch } from 'vue'
+
+import { menuInjectionKey } from '@/injection'
+import router from '@/router'
+import { useConfigureStore } from '@/stores/configure'
+import { useUserStore } from '@/stores/user'
+
+import type { MenuInst } from 'naive-ui'
+
+const menuInject = inject(menuInjectionKey, null)
+
+const configureStore = useConfigureStore()
+
+const userStore = useUserStore()
+
+const menuRef = useTemplateRef<MenuInst>('menuRef')
+
+const menuActiveKey = ref('')
+
+watch(
+  () => router.currentRoute.value,
+  (newValue) => {
+    menuActiveKey.value = newValue.name as string
+
+    menuRef.value?.showOption(menuActiveKey.value)
+  },
+  {
+    immediate: true,
+  },
+)
+
+onMounted(() => {
+  console.log({ node: menuRef.value })
+})
+</script>
+<template>
+  <NScrollbar>
+    <NMenu ref="menuRef" :collapsed-width="menuInject?.collapse.width"
+      :collapsed="configureStore.configure.menuCollapsed" :collapsed-icon-size="20" :value="menuActiveKey"
+      :options="userStore.menuList" />
+  </NScrollbar>
+</template>

+ 90 - 0
src/layout/aside/component/UserCard.vue

@@ -0,0 +1,90 @@
+<script setup lang="ts">
+import { useMessage, NDropdown, NAvatar } from 'naive-ui'
+import { h } from 'vue'
+
+import { ButtonAnimation } from '@/components'
+import DefaultAvatar from '@/components/Avatar.vue'
+import { useConfigureStore } from '@/stores/configure'
+import { useUserStore } from '@/stores/user'
+
+const configureStore = useConfigureStore()
+const userStore = useUserStore()
+const message = useMessage()
+
+const userDropdownOptions = [
+  {
+    icon: () => h('span', { class: 'i-ph:user size-5' }),
+    key: 'user',
+    label: '个人中心',
+  },
+  {
+    icon: () => h('span', { class: 'i-ph:sign-out size-5' }),
+    key: 'signout',
+    label: '退出登录',
+  },
+]
+
+const handleUserCardClick = () => {
+  message.info('你可以把它设计成有背景的User Card')
+}
+
+const onUserDropdownSelected = (key: string) => {
+  switch (key) {
+    case 'user':
+      message.info('点击了个人中心')
+      break
+    case 'signout':
+      userStore.cleanup()
+      break
+  }
+}
+</script>
+<template>
+  <div
+    class="mb-4 flex cursor-pointer items-center transition-[background-color,border-radius,margin,padding] duration-300 hover:bg-neutral-200/90 dark:hover:bg-neutral-750/65"
+    :class="configureStore.configure.menuCollapsed
+      ? 'mx-2 rounded'
+      : 'mx-4 rounded-xl bg-neutral-150 py-3.5 pr-2.5 pl-3.5 dark:bg-neutral-800'
+      " @click="handleUserCardClick">
+    <NDropdown :options="userDropdownOptions" show-arrow @select="onUserDropdownSelected" placement="right-end"
+      :disabled="!configureStore.configure.menuCollapsed">
+      <div
+        class="grid place-items-center overflow-hidden rounded-full transition-[margin,padding]-300 transition-ease-in-out"
+        :class="configureStore.configure.menuCollapsed ? 'mr-0 px-2 py-1.5' : 'mr-2'">
+        <div
+          class="flex items-center justify-center overflow-hidden transition-[height,width]-300 transition-ease-in-out"
+          :class="configureStore.configure.menuCollapsed ? 'size-8' : 'size-10'">
+          <NAvatar round src="src path" size="large" class="aspect-square" style="height: unset">
+            <template #fallback>
+              <DefaultAvatar class="text-primary" />
+            </template>
+          </NAvatar>
+        </div>
+      </div>
+    </NDropdown>
+    <Transition enter-active-class="transition-grid-template-columns-150 transition-ease-in-out"
+      leave-active-class="transition-grid-template-columns-150 transition-ease-in-out"
+      enter-from-class="grid-cols-[0fr]" leave-to-class="grid-cols-[0fr]" enter-to-class="grid-cols-[1fr]"
+      leave-from-class="grid-cols-[1fr]">
+      <div class="grid flex-1 overflow-hidden" v-show="!configureStore.configure.menuCollapsed">
+        <div class="flex min-w-0 items-center overflow-hidden">
+          <div class="flex flex-1 flex-col gap-y-px">
+            <span class="truncate text-sm">
+              {{ userStore.user.name }}
+            </span>
+            <span class="truncate text-xs text-neutral-450 dark:text-neutral-500">
+              这里应该写点什么
+            </span>
+          </div>
+
+          <NDropdown trigger="click" :options="userDropdownOptions" show-arrow @select="onUserDropdownSelected"
+            placement="top">
+            <ButtonAnimation animation="rotate" title="设置">
+              <span class="text-neutral-500 i-ph:gear dark:text-neutral-450" />
+            </ButtonAnimation>
+          </NDropdown>
+        </div>
+      </div>
+    </Transition>
+  </div>
+</template>

+ 45 - 0
src/layout/aside/index.vue

@@ -0,0 +1,45 @@
+<script setup lang="ts">
+import { computed, inject } from 'vue'
+
+import { menuInjectionKey } from '@/injection'
+import { useConfigureStore } from '@/stores/configure'
+
+import Menu from './component/Menu.vue'
+import UserCard from './component/UserCard.vue'
+
+defineOptions({
+  name: 'AsideLayout',
+})
+
+const menuInject = inject(menuInjectionKey, null)
+
+const configureStore = useConfigureStore()
+
+const menuCollapseWidth = computed(() =>
+  configureStore.configure.menuCollapsed
+    ? menuInject?.collapse.width
+    : menuInject?.collapse.maxWidth,
+)
+
+function handleCollapseClick() {
+  configureStore.modify({ menuCollapsed: !configureStore.configure.menuCollapsed })
+}
+</script>
+<template>
+  <div
+    class="relative flex h-full flex-col justify-between gap-y-4 border-r border-[var(--border-color)] bg-[var(--card-color)] transition-[background-color,border-color,width]-300 transition-ease-in-out"
+    :style="{
+      width: `${menuCollapseWidth}px`,
+    }">
+    <Menu />
+    <UserCard />
+    <div
+      class="absolute top-1/2 right-0 z-50 grid size-6 translate-x-1/2 -translate-y-1/2 cursor-pointer place-items-center rounded-full border border-[var(--border-color)] bg-white transition-[background-color,border-color]-300 transition-ease-in-out hover:bg-neutral-50 dark:bg-neutral-750 dark:hover:bg-neutral-700"
+      @click="handleCollapseClick">
+      <span class="size-4.5 transition-[color,rotate]-300 transition-ease-in-out i-ph:caret-left dark:text-neutral-400"
+        :class="{
+          'rotate-180': configureStore.configure.menuCollapsed,
+        }" />
+    </div>
+  </div>
+</template>

+ 414 - 0
src/layout/component/Tabs.vue

@@ -0,0 +1,414 @@
+<script setup lang="ts">
+import { isEmpty } from 'es-toolkit/compat'
+import { NButton, NDropdown, NEllipsis, NScrollbar } from 'naive-ui'
+import { storeToRefs } from 'pinia'
+import {
+  computed,
+  h,
+  inject,
+  nextTick,
+  onMounted,
+  reactive,
+  ref,
+  useTemplateRef,
+  watch,
+  watchEffect,
+} from 'vue'
+import { VueDraggable } from 'vue-draggable-plus'
+
+import { ButtonAnimation } from '@/components'
+import { tabsInjectionKey } from '@/injection'
+import router from '@/router'
+import { useConfigureStore } from '@/stores/configure'
+import { useTabsStore } from '@/stores/tabs'
+
+import type { Tab } from '@/stores/tabs'
+import type { DropdownOption } from 'naive-ui'
+
+type ContextMenuActions = {
+  close: () => void
+  closeAll: () => void
+  closeLeft: () => void
+  closeOther: () => void
+  closeRight: () => void
+  keepAlive: () => void
+  lock: () => void
+}
+
+const tabsInject = inject(tabsInjectionKey, null)
+
+const scrollbarRef = useTemplateRef<InstanceType<typeof NScrollbar>>('scrollbarRef')
+
+const tabsStore = useTabsStore()
+
+const configureStore = useConfigureStore()
+
+const { tabs, tabActiveKey, keepAliveTabs } = storeToRefs(tabsStore)
+
+const {
+  isLocked,
+  isPinned,
+  hasKeepAliveTab,
+  getUnlockedKeysBefore,
+  getUnlockedKeysAfter,
+  getUnlockedKeysExcept,
+  getUnlockedKeys,
+  setKeepAliveTab,
+  setLocked,
+  update,
+  remove,
+  removeBefore,
+  removeAfter,
+  removeExcept,
+  removeAll,
+} = tabsStore
+
+const tabPinnedList = ref<Tab[]>([])
+
+const tabList = ref<Tab[]>([])
+
+const isMounted = ref(false)
+
+const tabBackgroundTransitionClasses = reactive({
+  leaveToClass: '',
+  enterFromClass: 'scale-0 opacity-0',
+})
+
+const isTabDragging = ref(false)
+
+const showTabTooltip = ref(true)
+
+const showTabDropdown = ref(false)
+
+const tabContextMenu = ref<Tab | null>(null)
+
+const tabDropdownOptions = computed<DropdownOption[]>(() => {
+  const currentTab = tabContextMenu.value
+
+  if (!currentTab) {
+    return []
+  }
+
+  const { key, componentName } = currentTab
+
+  return [
+    {
+      disabled: isLocked(key) || isPinned(key),
+      icon: () =>
+        h('span', {
+          class: 'i-ph:x',
+        }),
+      key: 'close',
+      label: '关闭',
+    },
+    {
+      disabled: isEmpty(getUnlockedKeysExcept(key)),
+      icon: () =>
+        h('span', {
+          class: 'i-ph:arrows-out-line-horizontal',
+        }),
+      key: 'closeOther',
+      label: '关闭其他',
+    },
+    {
+      disabled: isEmpty(getUnlockedKeysBefore(key)),
+      icon: () =>
+        h('span', {
+          class: 'i-ph:arrow-line-left',
+        }),
+      key: 'closeLeft',
+      label: '关闭左侧',
+    },
+    {
+      disabled: isEmpty(getUnlockedKeysAfter(key)),
+      icon: () =>
+        h('span', {
+          class: 'i-ph:arrow-line-right',
+        }),
+      key: 'closeRight',
+      label: '关闭右侧',
+    },
+    {
+      disabled: isEmpty(getUnlockedKeys()),
+      icon: () =>
+        h('span', {
+          class: 'i-ph:arrows-horizontal',
+        }),
+      key: 'closeAll',
+      label: '关闭所有',
+    },
+    {
+      disabled: isEmpty(componentName),
+      icon: () =>
+        hasKeepAliveTab(componentName)
+          ? h('span', {
+            class: 'i-hugeicons:database-02',
+          })
+          : h('span', {
+            class: 'i-hugeicons:database-locked',
+          }),
+      key: 'keepAlive',
+      label: hasKeepAliveTab(componentName) ? '取消缓存' : '缓存标签页',
+    },
+    {
+      disabled: isPinned(key),
+      icon: () =>
+        isLocked(key)
+          ? h('span', {
+            class: 'i-ph:lock-simple-open',
+          })
+          : h('span', {
+            class: 'i-ph:lock-simple',
+          }),
+      key: 'lock',
+      label: isLocked(key) ? '解锁标签页' : '锁定标签页',
+    },
+  ]
+})
+
+const dropdownPosition = reactive({
+  x: 0,
+  y: 0,
+})
+
+const handleTabClick = (key: string) => {
+  router.push(key)
+}
+
+const handleTabCloseClick = (key: string) => {
+  remove(key)
+}
+
+const handleTabContextMenuClick = (e: MouseEvent, tab: Tab) => {
+  e.preventDefault()
+  tabContextMenu.value = tab
+  showTabDropdown.value = false
+  showTabTooltip.value = false
+
+  nextTick(() => {
+    showTabDropdown.value = true
+    dropdownPosition.x = e.clientX
+    dropdownPosition.y = e.clientY
+  })
+}
+
+const onTabDropdownClickOutside = () => {
+  showTabDropdown.value = false
+  showTabTooltip.value = true
+  tabContextMenu.value = null
+}
+
+const onTabDraggableStart = () => {
+  showTabDropdown.value = false
+  showTabTooltip.value = false
+}
+
+const onTabDraggableEnd = () => {
+  update([...tabPinnedList.value, ...tabList.value])
+  showTabTooltip.value = true
+}
+
+const onTabDraggableChoose = () => {
+  isTabDragging.value = true
+}
+
+const onTabDraggableUnchoose = () => {
+  isTabDragging.value = false
+}
+
+const onTabDropdownSelected = (key: keyof ContextMenuActions) => {
+  showTabDropdown.value = false
+  showTabTooltip.value = true
+
+  getTabContextMenuActions()?.[key]()
+}
+
+const onScrollbarWheeled = (e: WheelEvent) => {
+  if (!scrollbarRef.value) return
+
+  scrollbarRef.value.scrollBy({
+    left: (e.deltaY || e.deltaX) * 3,
+    behavior: 'smooth',
+  })
+}
+
+function getTabContextMenuActions(): ContextMenuActions | null {
+  const tabValue = tabContextMenu.value
+
+  if (!tabValue) {
+    return null
+  }
+
+  const { key, componentName } = tabValue
+
+  return {
+    close: () => {
+      remove(key)
+    },
+    closeAll: () => {
+      removeAll()
+    },
+    closeLeft: () => {
+      removeBefore(key)
+    },
+    closeOther: () => {
+      removeExcept(key)
+    },
+    closeRight: () => {
+      removeAfter(key)
+    },
+    keepAlive: () => {
+      if (componentName) {
+        setKeepAliveTab(componentName)
+      }
+    },
+    lock: () => {
+      setLocked(key)
+    },
+  }
+}
+
+function scrollToActiveTab() {
+  nextTick(() => {
+    document.querySelector('.tab-active')?.scrollIntoView({
+      behavior: 'smooth',
+    })
+  })
+}
+
+watch(
+  (): [Tab[], string] => [tabs.value, tabActiveKey.value],
+  ([newTabs, newActive], [oldTabs, oldActive]) => {
+    if (!newActive) {
+      tabBackgroundTransitionClasses.leaveToClass = 'scale-0 opacity-0'
+      return
+    }
+
+    if (!oldActive) {
+      tabBackgroundTransitionClasses.enterFromClass = 'scale-0 opacity-0'
+      return
+    }
+
+    const oldActiveIndex = oldTabs.findIndex((item) => item.key === oldActive)
+    const newActiveIndex = newTabs.findIndex((item) => item.key === newActive)
+
+    if (oldActiveIndex > newActiveIndex && newActiveIndex !== -1) {
+      tabBackgroundTransitionClasses.leaveToClass = '-translate-x-full'
+      tabBackgroundTransitionClasses.enterFromClass = 'translate-x-full'
+    } else {
+      tabBackgroundTransitionClasses.leaveToClass = 'translate-x-full'
+      tabBackgroundTransitionClasses.enterFromClass = '-translate-x-full'
+    }
+  },
+)
+
+watchEffect(() => {
+  tabList.value = tabs.value.filter((tab) => !tab.pinned)
+  tabPinnedList.value = tabs.value.filter((tab) => tab.pinned)
+})
+
+onMounted(() => {
+  nextTick(() => {
+    isMounted.value = true
+  })
+})
+</script>
+<template>
+  <div
+    class="flex min-h-0 overflow-hidden border-b border-[var(--border-color)] bg-[var(--card-color)] transition-[background-color,border-color]-300 transition-ease-in-out select-none">
+    <div class="flex shrink-0">
+      <div v-for="tab in tabPinnedList" :key="tab.key"
+        class="relative max-w-40 overflow-hidden border-r border-[var(--border-color)] transition-[background-color,border-color,max-width]-300 transition-ease-in-out hover:(bg-[var(--primary-color)] bg-op-6)"
+        :class="[
+          isTabDragging ? 'cursor-move' : 'cursor-pointer',
+          {
+            'tab-active bg-[var(--primary-color)] bg-op-6': tab.key === tabActiveKey,
+          },
+        ]" @click="handleTabClick(tab.key)" @contextmenu="(e) => handleTabContextMenuClick(e, tab)">
+        <Transition
+          enter-active-class="transition-[opacity,scale,translate]-300 transition-ease-in-out will-change-[opacity,transform,scale]"
+          leave-active-class="transition-[opacity,scale,translate]-300 transition-ease-in-out will-change-[opacity,transform,scale]"
+          :leave-to-class="tabBackgroundTransitionClasses.leaveToClass"
+          :enter-from-class="tabBackgroundTransitionClasses.enterFromClass" @after-enter="scrollToActiveTab">
+          <div v-if="tab.key === tabActiveKey && isMounted"
+            class="absolute inset-0 z-0 size-full border-t-[1.5px] border-primary bg-[var(--primary-color)] bg-op-6" />
+        </Transition>
+        <div class="relative flex items-center py-2.5 pl-4" :class="tab.pinned ? 'pr-4' : 'pr-2.5'">
+          <div class="mr-2 grid shrink-0 place-items-center overflow-hidden">
+            <span :class="tab.icon" />
+          </div>
+          <NEllipsis :tooltip="showTabTooltip">{{ tab.label }}</NEllipsis>
+        </div>
+      </div>
+    </div>
+    <NScrollbar ref="scrollbarRef" x-scrollable @wheel.passive="onScrollbarWheeled">
+      <VueDraggable class="flex" v-model="tabList" :animation="150" direction="horizontal" force-fallback :delay="300"
+        ghost-class="bg-[var(--primary-color)] bg-op-30" @start="onTabDraggableStart" @end="onTabDraggableEnd"
+        @choose="onTabDraggableChoose" @unchoose="onTabDraggableUnchoose">
+        <TransitionGroup enter-from-class="max-w-0" leave-to-class="max-w-0" @after-enter="scrollToActiveTab">
+          <div v-for="tab in tabList" :key="tab.key"
+            class="relative overflow-hidden border-r border-[var(--border-color)] transition-[background-color,border-color,max-width]-300 transition-ease-in-out hover:(bg-[var(--primary-color)] bg-op-6) [&:not(.max-w-0)]:max-w-40"
+            :class="[
+              isTabDragging ? 'cursor-move' : 'cursor-pointer',
+              {
+                'tab-active': tab.key === tabActiveKey,
+                group: !tab.locked && !configureStore.configure.showTabClose,
+              },
+            ]" @click="handleTabClick(tab.key)" @contextmenu="(e) => handleTabContextMenuClick(e, tab)">
+            <Transition
+              enter-active-class="transition-[opacity,scale,translate]-300 transition-ease-in-out will-change-[opacity,transform,scale]"
+              leave-active-class="transition-[opacity,scale,translate]-300 transition-ease-in-out will-change-[opacity,transform,scale]"
+              :leave-to-class="tabBackgroundTransitionClasses.leaveToClass"
+              :enter-from-class="tabBackgroundTransitionClasses.enterFromClass" @after-enter="scrollToActiveTab">
+              <div v-if="tab.key === tabActiveKey && isMounted"
+                class="absolute inset-0 z-0 size-full border-t-[1.5px] border-primary bg-[var(--primary-color)] bg-op-6" />
+            </Transition>
+            <div class="flex items-center py-2.5 pl-4" :class="tab.pinned ? 'pr-4' : 'pr-2.5'">
+              <div
+                class="relative flex items-center justify-center overflow-hidden transition-translate-300 transition-ease-in-out"
+                :class="{
+                  'translate-x-2.5': tab.locked || !configureStore.configure.showTabClose,
+                  'group-hover:translate-x-0':
+                    !tab.locked && !configureStore.configure.showTabClose,
+                }">
+                <div class="mr-2 grid shrink-0 place-items-center overflow-hidden">
+                  <span :class="[
+                    tab.icon,
+                    {
+                      'text-primary':
+                        tab.componentName && keepAliveTabs.includes(tab.componentName),
+                    },
+                  ]" />
+                </div>
+
+                <NEllipsis :tooltip="showTabTooltip">{{ tab.label }}</NEllipsis>
+              </div>
+
+              <div class="ml-1 flex transition-[opacity,scale]-300 transition-ease-in-out" :class="{
+                'scale-0 opacity-0': tab.locked || !configureStore.configure.showTabClose,
+                'group-hover:scale-100 group-hover:opacity-100':
+                  !tab.locked && !configureStore.configure.showTabClose,
+              }">
+                <NButton v-if="!tab.pinned" quaternary circle size="tiny" @click.stop="handleTabCloseClick(tab.key)"
+                  :disabled="tab.locked">
+                  <template #icon>
+                    <span class="i-ph:x" />
+                  </template>
+                </NButton>
+              </div>
+            </div>
+          </div>
+        </TransitionGroup>
+      </VueDraggable>
+    </NScrollbar>
+    <div class="flex items-center px-3">
+      <ButtonAnimation title="刷新" @click="tabsInject?.doRefresh(true)" animation="rotate">
+        <span class="size-5 i-ph:arrows-clockwise"></span>
+      </ButtonAnimation>
+    </div>
+    <NDropdown placement="bottom-start" trigger="manual" :x="dropdownPosition.x" :y="dropdownPosition.y"
+      :options="tabDropdownOptions" :show="showTabDropdown" @clickoutside="onTabDropdownClickOutside"
+      @select="onTabDropdownSelected">
+    </NDropdown>
+  </div>
+</template>

+ 15 - 0
src/layout/footer/index.vue

@@ -0,0 +1,15 @@
+<script setup lang="ts">
+defineOptions({
+  name: 'FooterLayout',
+})
+
+const APP_NAME = import.meta.env.VITE_GLOB_APP_NAME
+</script>
+<template>
+  <footer
+    class="min-h-0 border-t border-[var(--border-color)] bg-[var(--card-color)] transition-[background-color,border-color]-300 transition-ease-in-out">
+    <div class="flex items-center justify-center overflow-hidden py-1.5 text-xs">
+      <span>{{ APP_NAME }}</span>
+    </div>
+  </footer>
+</template>

+ 259 - 0
src/layout/header/Actions/component/ConfigureDrawer.vue

@@ -0,0 +1,259 @@
+<script setup lang="ts">
+import { throttle } from 'es-toolkit'
+import {
+  NColorPicker,
+  NDivider,
+  NDrawer,
+  NDrawerContent,
+  NSwitch,
+  useModal,
+  NScrollbar,
+} from 'naive-ui'
+import { h, ref } from 'vue'
+
+import packageJson from '@/../package.json'
+import { ButtonAnimation } from '@/components'
+import { useComponentThemeOverrides } from '@/composable/comp/useComponentThemeOverrides'
+import { usePersonalization } from '@/composable/usePersonalization'
+import { useConfigureStore } from '@/stores/configure'
+import themeColors from '@/utils/themeColor'
+
+import NoiseModal from './NoiseModal.vue'
+import WatermarkModal from './WatermarkModal.vue'
+
+const configureStore = useConfigureStore()
+
+const { color, setColor } = usePersonalization()
+
+const { modify, reset } = configureStore
+
+const modal = useModal()
+
+const { scrollbarInModal } = useComponentThemeOverrides()
+
+const showDrawer = ref(false)
+
+const colorSwatches = [
+  themeColors.red[500],
+  themeColors.orange[500],
+  themeColors.amber[500],
+  themeColors.yellow[500],
+  themeColors.lime[500],
+  themeColors.green[500],
+  themeColors.emerald[500],
+  themeColors.teal[500],
+  themeColors.cyan[500],
+  themeColors.sky[500],
+  themeColors.blue[500],
+  themeColors.indigo[500],
+  themeColors.violet[500],
+  themeColors.purple[500],
+  themeColors.fuchsia[500],
+  themeColors.pink[500],
+]
+
+const modifyColor = throttle((value: string) => {
+  setColor(value)
+}, 1000)
+
+const showWatermarkModal = () => {
+  modal.create({
+    title: '修改水印信息',
+    preset: 'dialog',
+    content: () =>
+      h(
+        NScrollbar,
+        {
+          themeOverrides: scrollbarInModal.value,
+          style: {
+            maxHeight: '400px',
+          },
+          contentClass: 'pr-3 py-6',
+        },
+        {
+          default: () => h(WatermarkModal),
+        },
+      ),
+    closable: true,
+    draggable: true,
+    showIcon: false,
+    zIndex: 99999,
+  })
+}
+
+const showNoiseModal = () => {
+  modal.create({
+    title: '修改磨砂细腻度',
+    preset: 'dialog',
+    content: () => h(NoiseModal),
+    closable: true,
+    draggable: true,
+    showIcon: false,
+    zIndex: 99999,
+  })
+}
+</script>
+<template>
+  <div>
+    <ButtonAnimation @click="showDrawer = true" title="侧边栏">
+      <span class="size-5 rotate-180 i-ph:sidebar-simple-duotone" />
+    </ButtonAnimation>
+    <NDrawer v-model:show="showDrawer" :auto-focus="false" :width="320" :theme-overrides="{
+      footerPadding: '14px 16px',
+    }">
+      <NDrawerContent :native-scrollbar="false">
+        <template #header>
+          <div class="flex items-center gap-x-1">
+            <span>系统设定</span>
+            <ButtonAnimation animation="rotate" @click="reset">
+              <span class="size-5 i-ph:arrow-clockwise" />
+            </ButtonAnimation>
+          </div>
+        </template>
+        <div>
+          <NDivider>主题颜色</NDivider>
+          <NColorPicker v-bind="$attrs" :default-value="color" :swatches="colorSwatches"
+            @update-value="(value) => modifyColor(value)" />
+        </div>
+        <div>
+          <NDivider>布局相关</NDivider>
+          <div class="flex flex-col gap-y-1.5">
+            <div class="flex items-center justify-between">
+              <span>展开侧边菜单</span>
+              <NSwitch :value="configureStore.configure.menuCollapsed" :checked-value="false" :unchecked-value="true"
+                @update-value="
+                  (value) =>
+                    modify({
+                      menuCollapsed: value,
+                    })
+                " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>显示顶部加载条</span>
+              <NSwitch :value="configureStore.configure.showTopLoadingBar" @update-value="
+                (value) =>
+                  modify({
+                    showTopLoadingBar: value,
+                  })
+              " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>显示Logo</span>
+              <NSwitch :value="configureStore.configure.showLogo" @update-value="
+                (value) =>
+                  modify({
+                    showLogo: value,
+                  })
+              " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>显示导航按钮</span>
+              <NSwitch :value="configureStore.configure.showNavigation" @update-value="
+                (value) =>
+                  modify({
+                    showNavigation: value,
+                  })
+              " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>显示面包屑</span>
+              <NSwitch :value="configureStore.configure.showBreadcrumb" @update-value="
+                (value) =>
+                  modify({
+                    showBreadcrumb: value,
+                  })
+              " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>显示标签页</span>
+              <NSwitch :value="configureStore.configure.showTabs" @update-value="
+                (value) =>
+                  modify({
+                    showTabs: value,
+                  })
+              " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>常显标签关闭按钮</span>
+              <NSwitch :value="configureStore.configure.showTabClose" @update-value="
+                (value) =>
+                  modify({
+                    showTabClose: value,
+                  })
+              " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>显示底部</span>
+              <NSwitch :value="configureStore.configure.showFooter" @update-value="
+                (value) =>
+                  modify({
+                    showFooter: value,
+                  })
+              " />
+            </div>
+          </div>
+        </div>
+        <div>
+          <NDivider>页面相关</NDivider>
+          <div class="flex items-center justify-between">
+            <div class="flex items-center gap-x-1">
+              <span>显示水印</span>
+              <ButtonAnimation size="small" @click="showWatermarkModal" label="修改">
+                <span class="size-4 i-ph:pencil-simple-line" />
+              </ButtonAnimation>
+            </div>
+            <NSwitch :value="configureStore.configure.showWatermark" @update-value="
+              (value) =>
+                modify({
+                  showWatermark: value,
+                })
+            " />
+          </div>
+          <div class="flex items-center justify-between">
+            <div class="flex items-center gap-x-1">
+              <span>显示磨砂效果</span>
+              <ButtonAnimation size="small" @click="showNoiseModal" label="修改">
+                <span class="size-4 i-ph:pencil-simple-line" />
+              </ButtonAnimation>
+            </div>
+            <NSwitch :value="configureStore.configure.showNoise" @update-value="
+              (value) =>
+                modify({
+                  showNoise: value,
+                })
+            " />
+          </div>
+          <div class="flex flex-col gap-y-1.5">
+            <div class="flex items-center justify-between">
+              <span>启用导航过渡效果</span>
+              <NSwitch :value="configureStore.configure.enableNavigationTransition" @update-value="
+                (value) =>
+                  modify({
+                    enableNavigationTransition: value,
+                  })
+              " />
+            </div>
+            <div class="flex items-center justify-between">
+              <span>文字可选中</span>
+              <NSwitch :value="configureStore.configure.enableTextSelect" @update-value="
+                (value) =>
+                  modify({
+                    enableTextSelect: value,
+                  })
+              " />
+            </div>
+          </div>
+        </div>
+        <template #footer>
+          <div class="flex w-full items-center justify-between">
+            <div class="flex items-center gap-x-1">
+              <span class="size-5 i-ph:gear-fine" />
+              <span class="leading-4">当前版本</span>
+            </div>
+            <span class="leading-4">{{ packageJson.version }}</span>
+          </div>
+        </template>
+      </NDrawerContent>
+    </NDrawer>
+  </div>
+</template>

+ 20 - 0
src/layout/header/Actions/component/FullScreen.vue

@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import { useFullscreen } from '@vueuse/core'
+
+import { ButtonAnimation } from '@/components'
+
+const { isFullscreen, enter, exit } = useFullscreen()
+</script>
+<template>
+  <div>
+    <ButtonAnimation
+      @click="isFullscreen ? exit() : enter()"
+      :title="isFullscreen ? '退出全屏' : '全屏'"
+    >
+      <span
+        class="size-5"
+        :class="isFullscreen ? 'i-ph:arrows-in' : 'i-ph:arrows-out'"
+      />
+    </ButtonAnimation>
+  </div>
+</template>

+ 42 - 0
src/layout/header/Actions/component/NoiseModal.vue

@@ -0,0 +1,42 @@
+<script setup lang="ts">
+import { NSlider, NInputNumber } from 'naive-ui'
+import { reactive, ref, watchEffect } from 'vue'
+
+import { useConfigureStore } from '@/stores/configure'
+
+import type { SliderProps } from 'naive-ui'
+
+const configureStore = useConfigureStore()
+
+const opacity = ref(0)
+
+const sliderRange = reactive({
+  step: 0.001,
+  min: 0,
+  max: 0.1,
+})
+
+const onSliderUpdata: SliderProps['onUpdateValue'] = (opacity) => {
+  configureStore.modify({
+    noiseOpacity: opacity,
+  })
+}
+
+watchEffect(() => {
+  opacity.value = configureStore.configure.noiseOpacity
+})
+</script>
+<template>
+  <div class="flex flex-col gap-y-4 pt-6 pb-4">
+    <NSlider
+      v-model:value="opacity"
+      v-bind="sliderRange"
+      @update-value="onSliderUpdata"
+    />
+    <NInputNumber
+      v-model:value="opacity"
+      size="small"
+      v-bind="sliderRange"
+    />
+  </div>
+</template>

+ 32 - 0
src/layout/header/Actions/component/SignOut.vue

@@ -0,0 +1,32 @@
+<script setup lang="ts">
+import { useDialog } from 'naive-ui'
+
+import { ButtonAnimation } from '@/components'
+import { useComponentModifier } from '@/composable/comp/useComponentModifier'
+import { useUserStore } from '@/stores/user'
+
+const userStore = useUserStore()
+const dialog = useDialog()
+
+const { getModalModifier } = useComponentModifier()
+
+const handleSignOutClick = () => {
+  dialog.info({
+    ...getModalModifier(),
+    title: '退出登录',
+    content: '确定要退出登录吗?',
+    positiveText: '确定',
+    negativeText: '取消',
+    onPositiveClick: cleanupUserInfo,
+  })
+}
+
+function cleanupUserInfo() {
+  userStore.cleanup()
+}
+</script>
+<template>
+  <ButtonAnimation @click="handleSignOutClick">
+    <span class="size-5 i-ph:sign-out" />
+  </ButtonAnimation>
+</template>

+ 75 - 0
src/layout/header/Actions/component/ThemeDropdown.vue

@@ -0,0 +1,75 @@
+<script setup lang="ts">
+import { NPopselect } from 'naive-ui'
+import { computed, h } from 'vue'
+
+import { ButtonAnimation } from '@/components'
+import { usePersonalization } from '@/composable/usePersonalization'
+
+import type { Theme } from '@/composable/usePersonalization'
+
+const { setTheme, theme } = usePersonalization()
+
+const themeDropdownOptions = [
+  {
+    icon: () => h('span', { class: 'i-ph:sun size-5' }),
+    iconName: 'i-ph:sun',
+    key: 'light',
+    value: 'light',
+    label: '浅色模式',
+  },
+  {
+    icon: () => h('span', { class: 'i-ph:moon size-5' }),
+    iconName: 'i-ph:moon',
+    key: 'dark',
+    value: 'dark',
+    label: '深色模式',
+  },
+  {
+    icon: () => h('span', { class: 'i-ph:desktop size-5' }),
+    iconName: 'i-ph:desktop',
+    key: 'system',
+    value: 'system',
+    label: '跟随系统',
+  },
+]
+
+const themeIconName = computed(
+  () =>
+    themeDropdownOptions.find((item) => item.key === theme.value)?.iconName ||
+    'i-ph:desktop',
+)
+
+const onThemePopselectUpdated = (key: Theme) => {
+  if (key === theme.value) return
+
+  setTheme(key)
+}
+
+function renderSelectLabel(option: (typeof themeDropdownOptions)[number]) {
+  return h(
+    'div',
+    {
+      class: 'flex items-center gap-x-2',
+    },
+    [option.icon(), option.label],
+  )
+}
+</script>
+<template>
+  <NPopselect
+    trigger="click"
+    v-model:value="theme"
+    :options="themeDropdownOptions"
+    :render-label="renderSelectLabel"
+    show-arrow
+    :to="false"
+    @update-value="onThemePopselectUpdated"
+  >
+    <ButtonAnimation title="主题">
+      <span
+        :class="themeIconName"
+        class="size-5"
+      />
+    </ButtonAnimation>
+  </NPopselect>
+</template>

+ 275 - 0
src/layout/header/Actions/component/WatermarkModal.vue

@@ -0,0 +1,275 @@
+<script setup lang="ts">
+import { throttle } from 'es-toolkit'
+import {
+  NForm,
+  NFormItem,
+  NInput,
+  NInputNumber,
+  NColorPicker,
+  NSlider,
+  NSwitch,
+  NSelect,
+} from 'naive-ui'
+import { ref } from 'vue'
+
+import { HintHelp } from '@/components'
+import { useConfigureStore } from '@/stores/configure'
+
+import type { ConfigureOptions } from '@/stores/configure'
+
+const configureStore = useConfigureStore()
+
+const watermarkOptions = ref<ConfigureOptions['watermarkOptions']>(
+  configureStore.configure.watermarkOptions,
+)
+
+const modifyWatermarkColor = throttle((color: string) => {
+  configureStore.modify({
+    watermarkOptions: {
+      fontColor: color,
+    },
+  })
+}, 100)
+</script>
+<template>
+  <NForm
+    :label-width="80"
+    :model="watermarkOptions"
+    :show-feedback="false"
+    class="space-y-4"
+  >
+    <NFormItem
+      label="水印内容"
+      path="content"
+    >
+      <NInput
+        type="textarea"
+        v-model:value="watermarkOptions.content"
+        clearable
+      />
+    </NFormItem>
+
+    <div class="flex gap-x-4">
+      <NFormItem
+        label="字体大小"
+        path="fontSize"
+        class="w-full"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.fontSize"
+          :min="8"
+          :max="32"
+        />
+      </NFormItem>
+      <NFormItem
+        label="文字颜色"
+        path="fontColor"
+        class="w-full"
+      >
+        <NColorPicker
+          :default-value="watermarkOptions.fontColor"
+          @update-value="
+            (value) => {
+              modifyWatermarkColor(value)
+            }
+          "
+        />
+      </NFormItem>
+      <NFormItem
+        label="字体风格"
+        path="fontStyle"
+        class="w-full"
+      >
+        <NSelect
+          v-model:value="watermarkOptions.fontStyle"
+          :options="[
+            { label: '正常', value: 'normal' },
+            { label: '斜体', value: 'italic' },
+            { label: '倾斜', value: 'oblique' },
+          ]"
+        />
+      </NFormItem>
+    </div>
+
+    <div class="flex gap-x-4">
+      <NFormItem
+        label="行高"
+        path="lineHeight"
+        class="w-full"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.lineHeight"
+          :min="1"
+        />
+      </NFormItem>
+      <NFormItem
+        label="字重"
+        path="fontWeight"
+        class="w-full"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.fontWeight"
+          :min="100"
+          :max="900"
+          :step="100"
+        />
+      </NFormItem>
+    </div>
+    <div class="flex gap-x-4">
+      <NFormItem
+        label="宽度"
+        path="width"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.width"
+          class="w-full"
+          :min="1"
+        />
+      </NFormItem>
+      <NFormItem
+        label="高度"
+        path="height"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.height"
+          class="w-full"
+          :min="1"
+        />
+      </NFormItem>
+    </div>
+
+    <div class="flex gap-x-4">
+      <NFormItem
+        label="X轴间隔"
+        path="xGap"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.xGap"
+          class="w-full"
+        />
+      </NFormItem>
+      <NFormItem
+        label="Y轴间隔"
+        path="yGap"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.yGap"
+          class="w-full"
+        />
+      </NFormItem>
+    </div>
+    <div class="flex gap-x-4">
+      <NFormItem
+        label="X轴偏移"
+        path="xoffset"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.xOffset"
+          class="w-full"
+        />
+      </NFormItem>
+      <NFormItem
+        label="Y轴偏移"
+        path="yGap"
+      >
+        <NInputNumber
+          v-model:value="watermarkOptions.yOffset"
+          class="w-full"
+        />
+      </NFormItem>
+    </div>
+    <div class="flex gap-x-4">
+      <NFormItem
+        label="旋转角度"
+        path="rotate"
+        class="w-full"
+      >
+        <NSlider
+          v-model:value="watermarkOptions.rotate"
+          :min="-90"
+          :max="90"
+        />
+      </NFormItem>
+      <NFormItem
+        label="整体旋转"
+        path="globalRotate"
+        class="w-full"
+      >
+        <NSlider
+          v-model:value="watermarkOptions.globalRotate"
+          :min="-180"
+          :max="180"
+        />
+      </NFormItem>
+      <NFormItem
+        label="跨越边界"
+        path="cross"
+        class="w-full"
+      >
+        <NSwitch v-model:value="watermarkOptions.cross" />
+      </NFormItem>
+    </div>
+    <NFormItem
+      label="图像地址"
+      path="image"
+    >
+      <NInput
+        type="textarea"
+        v-model:value="watermarkOptions.image"
+        clearable
+      />
+    </NFormItem>
+
+    <div class="flex gap-x-4">
+      <NFormItem
+        path="imageWidth"
+        class="w-full"
+        :show-label="false"
+      >
+        <div>
+          <HintHelp
+            label="图像宽度"
+            content="修改后需重新打开水印"
+            class="pb-1.5"
+          />
+          <NInputNumber v-model:value="watermarkOptions.imageWidth" />
+        </div>
+      </NFormItem>
+      <NFormItem
+        path="imageHeight"
+        class="w-full"
+        :show-label="false"
+      >
+        <div>
+          <HintHelp
+            label="图像高度"
+            content="修改后需重新打开水印"
+            class="pb-1.5"
+          />
+          <NInputNumber v-model:value="watermarkOptions.imageHeight" />
+        </div>
+      </NFormItem>
+
+      <NFormItem
+        path="imageOpacity"
+        class="w-full"
+        :show-label="false"
+      >
+        <div class="flex h-full flex-col">
+          <HintHelp
+            label="图像透明度"
+            content="修改后需重新打开水印"
+            class="pb-1.5"
+          />
+          <NSlider
+            v-model:value="watermarkOptions.imageOpacity"
+            :min="0"
+            :max="1"
+            :step="0.01"
+            class="mt-2"
+          />
+        </div>
+      </NFormItem>
+    </div>
+  </NForm>
+</template>

+ 23 - 0
src/layout/header/Actions/index.vue

@@ -0,0 +1,23 @@
+<script setup lang="ts">
+import { ButtonAnimation } from '@/components'
+
+import ConfigureDrawer from './component/ConfigureDrawer.vue'
+import FullScreen from './component/FullScreen.vue'
+import Signout from './component/SignOut.vue'
+import ThemeDropdown from './component/ThemeDropdown.vue'
+
+defineOptions({
+  name: 'Actions',
+})
+</script>
+<template>
+  <div class="flex items-center gap-x-3">
+    <ButtonAnimation :animation="false" tag="a" href="https://github.com/tenianon/lithe-admin" target="_blank">
+      <span class="i-mdi:github size-5" />
+    </ButtonAnimation>
+    <FullScreen />
+    <ThemeDropdown />
+    <ConfigureDrawer />
+    <Signout />
+  </div>
+</template>

+ 86 - 0
src/layout/header/Breadcrumb.vue

@@ -0,0 +1,86 @@
+<script setup lang="ts">
+import { isEmpty } from 'es-toolkit/compat'
+import { NDropdown } from 'naive-ui'
+import { computed, h } from 'vue'
+
+import router from '@/router'
+
+import type { DropdownProps } from 'naive-ui'
+import type { RouteRecordNameGeneric, RouteRecordRaw } from 'vue-router'
+
+const routerBreadcrumb = computed(() => {
+  return router.currentRoute.value.matched.filter((item) => item.name !== 'layout')
+})
+
+function resolveDropdownOptions(route: RouteRecordRaw[]): DropdownProps['options'] {
+  return route.map((item) => {
+    return {
+      label: item.meta?.label,
+      key: (item.name as string) || item.path,
+      icon: () => h('span', { class: `${item.meta?.icon} size-5` }),
+      children: !isEmpty(item.children)
+        ? resolveDropdownOptions(item.children as RouteRecordRaw[])
+        : undefined,
+    }
+  })
+}
+
+const onDropdownSelected: DropdownProps['onSelect'] = (key) => {
+  router.push({ name: key })
+}
+
+function isCurrentRoute(name: RouteRecordNameGeneric) {
+  return name === router.currentRoute.value.name
+}
+</script>
+<template>
+  <nav class="min-w-0">
+    <TransitionGroup
+      tag="ul"
+      class="flex"
+      enter-active-class="transition-grid-template-columns-300 transition-ease-in-out"
+      leave-active-class="transition-grid-template-columns-300 transition-ease-in-out"
+      enter-from-class="grid-cols-[0fr]"
+      leave-to-class="grid-cols-[0fr]"
+      enter-to-class="grid-cols-[1fr]"
+      leave-from-class="grid-cols-[1fr]"
+    >
+      <li
+        v-for="item in routerBreadcrumb"
+        :key="item.path"
+        class="grid shrink-0 justify-start overflow-hidden"
+      >
+        <div
+          class="flex min-w-0 items-center"
+          :class="{
+            'not-hover:text-[var(--text-color-3)]': !isCurrentRoute(item.name),
+          }"
+        >
+          <NDropdown
+            :options="resolveDropdownOptions(item.children)"
+            placement="bottom-start"
+            @select="onDropdownSelected"
+          >
+            <div
+              class="flex shrink-0 items-center gap-x-1.5 rounded px-1.5 py-1 transition-[background-color,color]"
+              :class="{
+                'cursor-pointer hover:bg-[var(--button-color-2-hover)]': !isCurrentRoute(item.name),
+              }"
+            >
+              <span
+                v-if="item.meta?.icon"
+                class="size-5"
+                :class="item.meta?.icon"
+              />
+              {{ item.meta?.label }}
+            </div>
+          </NDropdown>
+          <span
+            class="i-fluent:slash-forward-20-regular] w-3.5 text-[var(--text-color-3)]"
+            v-if="!isCurrentRoute(item.name)"
+          />
+        </div>
+      </li>
+    </TransitionGroup>
+  </nav>
+</template>

+ 41 - 0
src/layout/header/Logo.vue

@@ -0,0 +1,41 @@
+<script setup lang="ts">
+import { computed, inject } from 'vue'
+
+import { menuInjectionKey } from '@/injection'
+import { useConfigureStore } from '@/stores/configure'
+
+const APP_NAME = import.meta.env.VITE_GLOB_APP_NAME
+
+const menuInject = inject(menuInjectionKey, null)
+
+const configureStore = useConfigureStore()
+
+const collapseWidth = computed(() => {
+  return configureStore.configure.menuCollapsed
+    ? menuInject?.collapse.width
+    : menuInject?.collapse.maxWidth
+})
+</script>
+<template>
+  <div class="shrink-0 border-r border-[var(--border-color)] transition-[border-color,width]-300 transition-ease-in-out"
+    :style="{
+      width: `${collapseWidth}px`,
+    }">
+    <div class="flex h-full items-center justify-center transition-[opacity,padding]-300 transition-ease-in-out" :class="[
+      configureStore.configure.menuCollapsed ? 'px-0' : 'px-4',
+      {
+        'opacity-0': !configureStore.configure.showLogo,
+      },
+    ]">
+      <div class="size-10 shrink-0">
+        <div class="size-full rounded bg-[var(--primary-color)] bg-op-10"></div>
+      </div>
+      <div class="flex-1 overflow-hidden transition-[margin-left,max-width] duration-300"
+        :class="configureStore.configure.menuCollapsed ? 'ml-0 max-w-0' : 'ml-4 max-w-44'">
+        <h1 class="truncate text-xl">
+          {{ APP_NAME }}
+        </h1>
+      </div>
+    </div>
+  </div>
+</template>

+ 49 - 0
src/layout/header/Navigation.vue

@@ -0,0 +1,49 @@
+<script setup lang="ts">
+import { reactive, watch } from 'vue'
+
+import { ButtonAnimation } from '@/components'
+import router from '@/router'
+
+const navigationState = reactive({
+  canGoBack: true,
+  canGoForward: true,
+})
+
+const stopWatch = watch(
+  () => router.currentRoute.value,
+  () => {
+    if (!window.navigation) {
+      stopWatch()
+      return
+    }
+
+    navigationState.canGoBack = window.navigation.canGoBack
+    navigationState.canGoForward = window.navigation.canGoForward
+  },
+  {
+    immediate: true,
+  },
+)
+</script>
+<template>
+  <div class="min-w-0">
+    <div class="flex gap-x-0.5 pr-2">
+      <ButtonAnimation
+        size="medium"
+        @click="router.back()"
+        title="上一页"
+        :disabled="!navigationState.canGoBack"
+      >
+        <span class="size-4.5 i-ph:arrow-left" />
+      </ButtonAnimation>
+      <ButtonAnimation
+        size="medium"
+        @click="router.forward()"
+        title="下一页"
+        :disabled="!navigationState.canGoForward"
+      >
+        <span class="size-4.5 i-ph:arrow-right" />
+      </ButtonAnimation>
+    </div>
+  </div>
+</template>

+ 41 - 0
src/layout/header/index.vue

@@ -0,0 +1,41 @@
+<script setup lang="ts">
+import { useConfigureStore } from '@/stores/configure'
+
+import Actions from './Actions/index.vue'
+import Breadcrumb from './Breadcrumb.vue'
+import Logo from './Logo.vue'
+import Navigation from './Navigation.vue'
+
+defineOptions({
+  name: 'HeaderLayout',
+})
+
+const configureStore = useConfigureStore()
+</script>
+<template>
+  <header class="flex">
+    <Logo />
+    <div class="flex flex-1 items-center p-4">
+      <div class="flex flex-1 items-center">
+        <Transition enter-active-class="transition-grid-template-columns-300 transition-ease-in-out"
+          leave-active-class="transition-grid-template-columns-300 transition-ease-in-out"
+          enter-from-class="grid-cols-[0fr]" leave-to-class="grid-cols-[0fr]" enter-to-class="grid-cols-[1fr]"
+          leave-from-class="grid-cols-[1fr]">
+          <div v-if="configureStore.configure.showNavigation" class="grid overflow-hidden">
+            <Navigation />
+          </div>
+        </Transition>
+
+        <Transition enter-active-class="transition-grid-template-columns-300 transition-ease-in-out"
+          leave-active-class="transition-grid-template-columns-300 transition-ease-in-out"
+          enter-from-class="grid-cols-[0fr]" leave-to-class="grid-cols-[0fr]" enter-to-class="grid-cols-[1fr]"
+          leave-from-class="grid-cols-[1fr]">
+          <div v-if="configureStore.configure.showBreadcrumb" class="grid overflow-hidden">
+            <Breadcrumb />
+          </div>
+        </Transition>
+      </div>
+      <Actions />
+    </div>
+  </header>
+</template>

+ 98 - 0
src/layout/index.vue

@@ -0,0 +1,98 @@
+<script setup lang="ts">
+import { isEmpty } from 'es-toolkit/compat'
+import { NScrollbar } from 'naive-ui'
+import { provide, reactive, ref } from 'vue'
+
+import texturePng from '@/assets/texture.png'
+import { EmptyPlaceholder } from '@/components'
+import { useComponentThemeOverrides } from '@/composable/comp/useComponentThemeOverrides'
+import { menuInjectionKey, tabsInjectionKey } from '@/injection'
+import { useConfigureStore } from '@/stores/configure'
+import { useTabsStore } from '@/stores/tabs'
+import { clearServiceQueue } from '@/service'
+
+import AsideLayout from './aside/index.vue'
+import Tabs from './component/Tabs.vue'
+import FooterLayout from './footer/index.vue'
+import HeaderLayout from './header/index.vue'
+import MainLayout from './main/index.vue'
+
+import type { TabsInjection } from '@/injection'
+
+defineOptions({
+  name: 'Layout',
+})
+
+const tabsStore = useTabsStore()
+const configureStore = useConfigureStore()
+const { scrollbarInMainLayout } = useComponentThemeOverrides()
+
+const shouldRefresh = ref(false)
+
+const menuCollapse = reactive({
+  width: 64,
+  maxWidth: 272,
+})
+
+const doRefresh: TabsInjection['doRefresh'] = (value) => {
+  shouldRefresh.value = value
+  if (value) {
+    // 手动刷新,清空请求队列
+    clearServiceQueue()
+  }
+}
+
+provide(menuInjectionKey, {
+  collapse: menuCollapse,
+})
+
+provide(tabsInjectionKey, {
+  shouldRefresh,
+  doRefresh,
+})
+</script>
+<template>
+  <div class="flex h-dvh flex-col overflow-hidden">
+    <div
+      class="border-b border-[var(--border-color)] bg-[var(--card-color)] transition-[background-color,border-color]-300 transition-ease-in-out">
+      <HeaderLayout />
+    </div>
+    <div class="flex flex-1 overflow-hidden">
+      <AsideLayout />
+      <div class="relative flex flex-1 flex-col overflow-x-hidden">
+        <Transition enter-active-class="transition-grid-template-rows-300 transition-ease-in-out"
+          leave-active-class="transition-grid-template-rows-300 transition-ease-in-out"
+          enter-from-class="grid-rows-[0fr]" leave-to-class="grid-rows-[0fr]" enter-to-class="grid-rows-[1fr]"
+          leave-from-class="grid-rows-[1fr]">
+          <div v-if="!isEmpty(tabsStore.tabs) && configureStore.configure.showTabs"
+            class="grid shrink-0 items-baseline overflow-hidden">
+            <Tabs />
+          </div>
+        </Transition>
+        <NScrollbar container-class="main-container" :container-style="{
+          backgroundImage: `url(${texturePng})`,
+        }" :theme-overrides="scrollbarInMainLayout">
+          <MainLayout />
+        </NScrollbar>
+        <EmptyPlaceholder :show="isEmpty(tabsStore.tabs)" description="空标签页" size="huge">
+          <template #icon>
+            <div class="grid place-items-center">
+              <span class="i-ph:rectangle" />
+            </div>
+          </template>
+
+          <template #content>
+            <Transition enter-active-class="transition-grid-template-rows-300 transition-ease-in-out"
+              leave-active-class="transition-grid-template-rows-300 transition-ease-in-out"
+              enter-from-class="grid-rows-[0fr]" leave-to-class="grid-rows-[0fr]" enter-to-class="grid-rows-[1fr]"
+              leave-from-class="grid-rows-[1fr]">
+              <div v-if="configureStore.configure.showFooter" class="grid shrink-0 items-baseline overflow-hidden">
+                <FooterLayout />
+              </div>
+            </Transition>
+          </template>
+        </EmptyPlaceholder>
+      </div>
+    </div>
+  </div>
+</template>

+ 302 - 0
src/layout/main/index.vue

@@ -0,0 +1,302 @@
+<script setup lang="ts">
+import { isEmpty } from 'es-toolkit/compat'
+import { storeToRefs } from 'pinia'
+import { inject, nextTick, onMounted, ref, watch } from 'vue'
+import { RouterView } from 'vue-router'
+
+import { tabsInjectionKey } from '@/injection'
+import router from '@/router'
+import { useConfigureStore } from '@/stores/configure'
+import { useTabsStore } from '@/stores/tabs'
+
+import type { Tab } from '@/stores/tabs'
+import type { RouteLocationNormalizedLoaded } from 'vue-router'
+
+defineOptions({
+  name: 'MainLayout',
+})
+
+const tabsInject = inject(tabsInjectionKey, null)
+
+const tabsStore = useTabsStore()
+
+const configureStore = useConfigureStore()
+
+const { tabs, tabActiveKey, keepAliveTabs } = storeToRefs(tabsStore)
+
+const { configure, isNavigating } = storeToRefs(configureStore)
+
+const { create, setActive } = tabsStore
+
+const isMounted = ref(false)
+
+const transitionName = ref('')
+
+let oldTabs: Tab[] = []
+
+function createTab(route: RouteLocationNormalizedLoaded) {
+  const {
+    pinned,
+    componentName,
+    icon = 'i-ph:browser',
+    label: defaultLabel = '未命名标签',
+    renderTabLabel,
+  } = route.meta
+
+  const { fullPath, name, params } = route
+
+  const label = renderTabLabel ? renderTabLabel(params) : defaultLabel
+
+  create({
+    componentName,
+    icon,
+    name,
+    key: fullPath,
+    label,
+    pinned,
+  })
+}
+
+watch(
+  (): [Tab[], string] => [tabs.value, tabActiveKey.value],
+  ([newTabs, newActive], [, oldActive]) => {
+    if (tabActiveKey.value) {
+      router.push(tabActiveKey.value)
+    }
+
+    if (!configure.value.enableNavigationTransition) return
+
+    if (!configure.value.showTabs) {
+      transitionName.value = 'scale'
+      return
+    }
+
+    const oldActiveIndex = oldTabs.findIndex((item) => item.key === oldActive)
+    const newActiveIndex = newTabs.findIndex((item) => item.key === newActive)
+
+    if (oldTabs.length < newTabs.length || oldActiveIndex === -1 || newActiveIndex === -1) {
+      transitionName.value = 'scale'
+    } else if (oldTabs.length > newTabs.length) {
+      transitionName.value =
+        oldActiveIndex > newActiveIndex ? 'scale-slider-left' : 'scale-slider-right'
+    } else if (oldActive !== newActive) {
+      transitionName.value = oldActiveIndex > newActiveIndex ? 'slider-left' : 'slider-right'
+    }
+
+    oldTabs = [...newTabs]
+  },
+)
+
+watch(
+  () => tabsInject?.shouldRefresh.value,
+  (shouldRefresh) => {
+    if (shouldRefresh) {
+      transitionName.value = 'shake'
+      nextTick(() => {
+        tabsInject?.doRefresh(false)
+      })
+    }
+  },
+)
+
+watch(
+  (): [RouteLocationNormalizedLoaded, boolean] => [router.currentRoute.value, isNavigating.value],
+  ([newRoute, isNavigating], [oldRoute]) => {
+    const { name, meta } = newRoute
+    const { showTab, enableMultiTab } = meta
+    const findTab = tabs.value.find((item) => item.name === name)
+
+    if (!isNavigating && newRoute.fullPath !== oldRoute?.fullPath) {
+      if (showTab && (!findTab || enableMultiTab)) {
+        createTab(newRoute)
+      } else if (!isEmpty(findTab)) {
+        setActive(findTab.key)
+      } else {
+        setActive('')
+      }
+    }
+  },
+)
+
+onMounted(() => {
+  const currentRoute = router.currentRoute.value
+  const { name, meta } = currentRoute
+
+  const findTab = tabs.value.find((item) => item.name === name)
+  const { showTab, enableMultiTab: multiTab } = meta
+
+  if (name === 'layout' && tabActiveKey.value) {
+    router.replace(tabActiveKey.value)
+  } else if (showTab && (!findTab || multiTab)) {
+    createTab(currentRoute)
+  } else if (!isEmpty(findTab)) {
+    setActive(findTab.key)
+  }
+
+  nextTick(() => {
+    isMounted.value = true
+    transitionName.value = 'scale'
+  })
+
+  oldTabs = [...tabs.value]
+})
+</script>
+<template>
+  <RouterView v-if="configure.enableNavigationTransition" v-slot="{ Component, route }">
+    <Transition :name="transitionName">
+      <KeepAlive :include="keepAliveTabs">
+        <component :is="Component" v-if="isMounted && !tabsInject?.shouldRefresh.value"
+          :key="route.path + JSON.stringify(route.query)" />
+      </KeepAlive>
+    </Transition>
+  </RouterView>
+  <RouterView v-else />
+</template>
+<style scoped>
+.slider-left-enter-active,
+.slider-right-enter-active,
+.slider-left-leave-active,
+.slider-right-leave-active {
+  position: absolute;
+  width: 100%;
+  transition: translate 500ms var(--cubic-bezier-ease-in-out) 75ms;
+  will-change: translate;
+}
+
+.slider-left-enter-from,
+.slider-right-leave-to {
+  translate: -100%;
+}
+
+.slider-left-leave-to,
+.slider-right-enter-from {
+  translate: 100%;
+}
+
+.slider-left-enter-to,
+.slider-left-leave-from,
+.slider-right-enter-to,
+.slider-right-leave-from {
+  translate: 0;
+}
+
+.scale-slider-left-enter-active,
+.scale-slider-left-leave-active,
+.scale-slider-right-enter-active,
+.scale-slider-right-leave-active {
+  position: absolute;
+  width: 100%;
+  transition:
+    scale 500ms var(--cubic-bezier-ease-in-out) 75ms,
+    translate 500ms var(--cubic-bezier-ease-in-out) 75ms,
+    opacity 300ms var(--cubic-bezier-ease-in-out) 75ms;
+  will-change: scale, translate, opacity;
+}
+
+.scale-slider-left-leave-to,
+.scale-slider-right-leave-to {
+  scale: 0.5;
+  opacity: 0;
+}
+
+.scale-slider-left-enter-from {
+  translate: -100%;
+}
+
+.scale-slider-right-enter-from {
+  translate: 100%;
+}
+
+.scale-enter-active,
+.scale-leave-active {
+  position: absolute;
+  width: 100%;
+  transition:
+    scale 300ms var(--cubic-bezier-ease-in-out) 75ms,
+    opacity 300ms var(--cubic-bezier-ease-in-out) 75ms;
+  will-change: scale, opacity;
+}
+
+.scale-enter-from,
+.scale-leave-to {
+  scale: 0.5;
+  opacity: 0;
+}
+
+/* shake */
+.shake-enter-active {
+  animation: shake var(--cubic-bezier-ease-in-out) 500ms;
+}
+
+@keyframes shake {
+  0% {
+    translate: 0 0;
+    rotate: 0deg;
+  }
+
+  8% {
+    translate: -2px -1px;
+    rotate: -0.6deg;
+  }
+
+  16% {
+    translate: 1px -2px;
+    rotate: 0.9deg;
+  }
+
+  24% {
+    translate: -1px 2px;
+    rotate: -0.5deg;
+  }
+
+  32% {
+    translate: 2px -1px;
+    rotate: 0.7deg;
+  }
+
+  40% {
+    translate: -1px -2px;
+    rotate: -0.8deg;
+  }
+
+  48% {
+    translate: 1px 2px;
+    rotate: 0.4deg;
+  }
+
+  56% {
+    translate: -2px 1px;
+    rotate: -0.7deg;
+  }
+
+  64% {
+    translate: 2px -1px;
+    rotate: 0.8deg;
+  }
+
+  72% {
+    translate: -1px -1px;
+    rotate: -0.5deg;
+  }
+
+  80% {
+    translate: 2px 1px;
+    rotate: 0.6deg;
+  }
+
+  88% {
+    translate: -1px 1px;
+    rotate: -0.4deg;
+  }
+
+  96% {
+    translate: 1px -1px;
+    rotate: 0.3deg;
+  }
+
+  100% {
+    translate: 0 0;
+    rotate: 0deg;
+  }
+}
+</style>

+ 42 - 0
src/main.ts

@@ -0,0 +1,42 @@
+import './assets/main.css'
+// unocss
+import 'virtual:uno.css'
+
+import { createApp } from 'vue'
+import App from '@/App.vue'
+import router from '@/router'
+// pinia store
+import pinia from '@/stores'
+
+// errorHandler & warnHandler
+import errorHandler from '@/utils/errorHandler'
+import warnHandler from '@/utils/warnHandler'
+
+import { InitStores } from '@/stores/initStores'
+
+async function setupApp() {
+  const app = createApp(App)
+
+  app.use(pinia)
+  app.use(router)
+
+  // 初始化Stores
+  InitStores()
+
+  await router.isReady()
+
+  console.log({ env: import.meta.env })
+  console.log({ routes: router.options.routes })
+
+  if (window.loaderElement) {
+    window.loaderElement.remove()
+    window.loaderElement = null
+  }
+
+  app.config.errorHandler = errorHandler
+  app.config.warnHandler = warnHandler
+
+  app.mount('#app')
+}
+
+setupApp()

+ 44 - 0
src/main.ts.bak

@@ -0,0 +1,44 @@
+// import './assets/main.css'
+
+import { createApp } from 'vue'
+
+import App from '@/App.vue'
+import router from '@/router/index_bak'
+
+// Prime Vue
+import PrimeVue from 'primevue/config'
+import ConfirmationService from 'primevue/confirmationservice'
+import ToastService from 'primevue/toastservice'
+import Aura from '@primeuix/themes/aura'
+// unocss
+import 'virtual:uno.css'
+// pinia store
+import pinia from '@/stores'
+// errorHandler & warnHandler
+import errorHandler from '@/utils/errorHandler'
+import warnHandler from '@/utils/warnHandler'
+
+import '@/assets/styles.scss'
+
+import '@/assets/tailwindcss-primeui/index.css'
+
+const app = createApp(App)
+
+app.use(pinia)
+app.use(router)
+app
+  .use(PrimeVue, {
+    theme: {
+      preset: Aura,
+      options: {
+        darkModeSelector: '.app-dark',
+      },
+    },
+  })
+  .use(ConfirmationService)
+  .use(ToastService)
+
+app.config.errorHandler = errorHandler
+app.config.warnHandler = warnHandler
+
+app.mount('#app')

Vissa filer visades inte eftersom för många filer har ändrats