Browse Source

初始化

黄琪勇 3 months ago
commit
06b25593ce
100 changed files with 19236 additions and 0 deletions
  1. 20 0
      .editorconfig
  2. 77 0
      .eslintrc.cjs
  3. 30 0
      .gitignore
  4. 7 0
      .prettierrc
  5. 2 0
      env.d.ts
  6. 62 0
      index.html
  7. 9173 0
      package-lock.json
  8. 70 0
      package.json
  9. BIN
      public/favicon.ico
  10. BIN
      public/icons/android-chrome-192x192.png
  11. BIN
      public/icons/android-chrome-512x512.png
  12. BIN
      public/icons/android-chrome-maskable-192x192.png
  13. BIN
      public/icons/android-chrome-maskable-512x512.png
  14. BIN
      public/icons/apple-touch-icon-152x152.png
  15. BIN
      public/icons/favicon-16x16.png
  16. BIN
      public/icons/favicon-32x32.png
  17. 54 0
      src/App.vue
  18. BIN
      src/assets/fonts/仓耳小丸子.woff2
  19. BIN
      src/assets/fonts/优设标题黑.woff2
  20. BIN
      src/assets/fonts/字制区喜脉体.woff2
  21. BIN
      src/assets/fonts/峰广明锐体.woff2
  22. BIN
      src/assets/fonts/得意黑.woff2
  23. BIN
      src/assets/fonts/摄图摩登小方体.woff2
  24. BIN
      src/assets/fonts/站酷快乐体.woff2
  25. BIN
      src/assets/fonts/素材集市康康体.woff2
  26. BIN
      src/assets/fonts/素材集市酷方体.woff2
  27. BIN
      src/assets/fonts/途牛类圆体.woff2
  28. BIN
      src/assets/fonts/锐字真言体.woff2
  29. 9 0
      src/assets/styles/font.scss
  30. 138 0
      src/assets/styles/global.scss
  31. 42 0
      src/assets/styles/mixin.scss
  32. 99 0
      src/assets/styles/prosemirror.scss
  33. 13 0
      src/assets/styles/variable.scss
  34. 7 0
      src/components.d.ts
  35. 116 0
      src/components/Button.vue
  36. 86 0
      src/components/ButtonGroup.vue
  37. 109 0
      src/components/Checkbox.vue
  38. 21 0
      src/components/CheckboxButton.vue
  39. 42 0
      src/components/ColorButton.vue
  40. 107 0
      src/components/ColorPicker/Alpha.vue
  41. 60 0
      src/components/ColorPicker/Checkboard.vue
  42. 69 0
      src/components/ColorPicker/EditableInput.vue
  43. 117 0
      src/components/ColorPicker/Hue.vue
  44. 108 0
      src/components/ColorPicker/Saturation.vue
  45. 443 0
      src/components/ColorPicker/index.vue
  46. 137 0
      src/components/Contextmenu/MenuContent.vue
  47. 80 0
      src/components/Contextmenu/index.vue
  48. 14 0
      src/components/Contextmenu/types.ts
  49. 34 0
      src/components/Divider.vue
  50. 126 0
      src/components/Drawer.vue
  51. 45 0
      src/components/FileInput.vue
  52. 66 0
      src/components/FullscreenSpin.vue
  53. 150 0
      src/components/GradientBar.vue
  54. 132 0
      src/components/Input.vue
  55. 57 0
      src/components/LaTeXEditor/FormulaContent.vue
  56. 20 0
      src/components/LaTeXEditor/SymbolContent.vue
  57. 5 0
      src/components/LaTeXEditor/hfmath.ts
  58. 265 0
      src/components/LaTeXEditor/index.vue
  59. 182 0
      src/components/Message.vue
  60. 154 0
      src/components/Modal.vue
  61. 220 0
      src/components/MoveablePanel.vue
  62. 201 0
      src/components/NumberInput.vue
  63. 103 0
      src/components/Popover.vue
  64. 38 0
      src/components/PopoverMenuItem.vue
  65. 26 0
      src/components/RadioButton.vue
  66. 35 0
      src/components/RadioGroup.vue
  67. 204 0
      src/components/Select.vue
  68. 54 0
      src/components/SelectGroup.vue
  69. 280 0
      src/components/Slider.vue
  70. 84 0
      src/components/Switch.vue
  71. 108 0
      src/components/Tabs.vue
  72. 92 0
      src/components/TextArea.vue
  73. 38 0
      src/components/TextColorButton.vue
  74. 359 0
      src/components/WritingBoard.vue
  75. 234 0
      src/configs/animation.ts
  76. 70 0
      src/configs/chart.ts
  77. 22 0
      src/configs/element.ts
  78. 44 0
      src/configs/font.ts
  79. 129 0
      src/configs/hotkey.ts
  80. 181 0
      src/configs/imageClip.ts
  81. 274 0
      src/configs/latex.ts
  82. 39 0
      src/configs/lines.ts
  83. 1038 0
      src/configs/shapes.ts
  84. 1 0
      src/configs/storage.ts
  85. 59 0
      src/configs/symbol.ts
  86. 93 0
      src/configs/theme.ts
  87. 16 0
      src/global.d.ts
  88. 106 0
      src/hooks/useAddSlidesOrElements.ts
  89. 177 0
      src/hooks/useAlignActiveElement.ts
  90. 80 0
      src/hooks/useAlignElementToCanvas.ts
  91. 91 0
      src/hooks/useCombineElement.ts
  92. 55 0
      src/hooks/useCopyAndPasteElement.ts
  93. 325 0
      src/hooks/useCreateElement.ts
  94. 44 0
      src/hooks/useDeleteElement.ts
  95. 855 0
      src/hooks/useExport.ts
  96. 320 0
      src/hooks/useGlobalHotkey.ts
  97. 35 0
      src/hooks/useHideElement.ts
  98. 27 0
      src/hooks/useHistorySnapshot.ts
  99. 494 0
      src/hooks/useImport.ts
  100. 37 0
      src/hooks/useLink.ts

+ 20 - 0
.editorconfig

@@ -0,0 +1,20 @@
+# http://editorconfig.org
+root = true
+
+[*]
+#缩进风格:空格
+indent_style = space
+#缩进大小
+indent_size = 2
+#换行符CRLF
+end_of_line = CRLF
+#字符集utf-8
+charset = utf-8
+#是否删除行尾的空格
+trim_trailing_whitespace = true
+#是否在文件的最后插入一个空行
+insert_final_newline = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 77 - 0
.eslintrc.cjs

@@ -0,0 +1,77 @@
+/* eslint-env node */
+require('@rushstack/eslint-patch/modern-module-resolution')
+
+module.exports = {
+  root: true,
+  extends: [
+    'plugin:vue/vue3-essential',
+    'eslint:recommended',
+    '@vue/eslint-config-typescript'
+  ],
+  parserOptions: {
+    ecmaVersion: 'latest'
+  },
+  rules: {
+    'curly': ['error', 'multi-line'],
+    'eqeqeq': ['error', 'always'],
+    'semi': ['error', 'never'],
+    'indent': ['error', 2, { 
+      'SwitchCase': 1,
+    }],
+    'quotes': ['error', 'single', {
+      'avoidEscape': true,
+      'allowTemplateLiterals': true,
+    }],
+    'key-spacing': ['error', {
+      'beforeColon': false,
+      'afterColon': true,
+      'mode': 'strict',
+    }],
+    'no-empty': 'error',
+    'no-else-return': 'error',
+    'no-multi-spaces': 'error',
+    'require-await': 'error',
+    'brace-style': ['error', 'stroustrup'],
+    'spaced-comment': ['error', 'always'],
+    'arrow-spacing': 'error',
+    'no-duplicate-imports': 'error',
+    'comma-spacing': ['error', {
+      'before': false,
+      'after': true,
+    }],
+    'default-case': 'error',
+    'consistent-this': ['error', '_this'],
+    'max-depth': ['error', 8],
+    'max-lines': ['error', 1000],
+    'no-multi-str': 'error',
+    'space-infix-ops': 'error',
+    'space-before-blocks': ['error', 'always'],
+    'space-before-function-paren': ['error', {
+      'named': 'never',
+      'anonymous': 'never',
+      'asyncArrow': 'always',
+    }],
+    'keyword-spacing': ['error'],
+    'prefer-const': 'error',
+    'no-useless-return': 'error',
+    'array-bracket-spacing': 'error',
+    'no-useless-escape': 'off',
+    'no-eval': 'error',
+    'no-var': 'error',
+    'no-with': 'error',
+    'no-alert': 'warn',
+    'no-console': 'warn',
+    'no-debugger': 'error',
+    '@typescript-eslint/explicit-module-boundary-types': 'off',
+    '@typescript-eslint/ban-types': ['error', {
+      'extendDefaults': true,
+      'types': {
+        '{}': false,
+      },
+    }],
+    '@typescript-eslint/no-non-null-assertion': 'off',
+    '@typescript-eslint/consistent-type-imports': 'error',
+    'vue/multi-word-component-names': 'off',
+    'vue/no-reserved-component-names': 'off',
+  }
+}

+ 30 - 0
.gitignore

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

+ 7 - 0
.prettierrc

@@ -0,0 +1,7 @@
+{
+  "singleQuote": true,
+  "trailingComma": "none",
+  "printWidth": 150,
+  "semi": false,
+  "arrowParens": "avoid"
+}

+ 2 - 0
env.d.ts

@@ -0,0 +1,2 @@
+// eslint-disable-next-line spaced-comment
+/// <reference types="vite/client" />

+ 62 - 0
index.html

@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="UTF-8">
+    <link rel="icon" href="/favicon.ico">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+    <meta name="renderer" content="webkit">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="description" content="PPTist:基于 Vue3.x + TypeScript 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能,实现在线PPT的编辑、演示。支持导出PPT文件。" />
+    <meta name="keywords" content="pptist,ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" />
+    <title>PPTist - 在线演示文稿</title>
+
+    <style>
+      .first-screen-loading {
+        width: 200px;
+        height: 200px;
+        position: fixed;
+        top: 50%;
+        left: 50%;
+        margin-top: -100px;
+        margin-left: -100px;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+      }
+      .first-screen-loading-spinner {
+        width: 36px;
+        height: 36px;
+        border: 3px solid #d14424;
+        border-top-color: transparent;
+        border-radius: 50%;
+        animation: spinner .8s linear infinite;
+      }
+      .first-screen-loading-text {
+        margin-top: 20px;
+        color: #d14424;
+      }
+      @keyframes spinner {
+        0% {
+          transform: rotate(0deg);
+        }
+        100% {
+          transform: rotate(360deg);
+        }
+      }
+    </style>
+  </head>
+  <body>
+    <div id="app">
+      <div class="first-screen-loading">
+        <div class="first-screen-loading-spinner"></div>
+        <div class="first-screen-loading-text">正在加载中,请稍等 ...</div>
+      </div>
+    </div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+
+  <script>
+    document.oncontextmenu = e => e.preventDefault()
+  </script>
+</html>

+ 9173 - 0
package-lock.json

@@ -0,0 +1,9173 @@
+{
+  "name": "pptist",
+  "version": "1.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "pptist",
+      "version": "1.0.0",
+      "dependencies": {
+        "@icon-park/vue-next": "^1.4.2",
+        "animate.css": "^4.1.1",
+        "clipboard": "^2.0.11",
+        "crypto-js": "^4.2.0",
+        "dexie": "3.0.3",
+        "echarts": "^5.5.1",
+        "file-saver": "^2.0.5",
+        "hfmath": "^0.0.2",
+        "html-to-image": "^1.11.11",
+        "lodash": "^4.17.21",
+        "mitt": "^3.0.1",
+        "nanoid": "^5.0.7",
+        "number-precision": "^1.6.0",
+        "pinia": "^2.1.7",
+        "pptxgenjs": "^3.12.0",
+        "pptxtojson": "^1.0.3",
+        "prosemirror-commands": "^1.6.0",
+        "prosemirror-dropcursor": "^1.8.1",
+        "prosemirror-gapcursor": "^1.3.2",
+        "prosemirror-history": "^1.3.2",
+        "prosemirror-inputrules": "^1.4.0",
+        "prosemirror-keymap": "^1.2.2",
+        "prosemirror-model": "^1.22.2",
+        "prosemirror-schema-basic": "^1.2.3",
+        "prosemirror-schema-list": "^1.4.1",
+        "prosemirror-state": "^1.4.3",
+        "prosemirror-view": "^1.33.9",
+        "svg-arc-to-cubic-bezier": "^3.2.0",
+        "svg-pathdata": "^7.1.0",
+        "tinycolor2": "^1.6.0",
+        "tippy.js": "^6.3.7",
+        "vue": "^3.4.34",
+        "vuedraggable": "^4.1.0"
+      },
+      "devDependencies": {
+        "@commitlint/cli": "^18.4.3",
+        "@commitlint/config-conventional": "^18.4.3",
+        "@rushstack/eslint-patch": "^1.3.3",
+        "@tsconfig/node18": "^18.2.2",
+        "@types/crypto-js": "^4.2.1",
+        "@types/file-saver": "^2.0.7",
+        "@types/lodash": "^4.14.202",
+        "@types/node": "^18.19.3",
+        "@types/svg-arc-to-cubic-bezier": "^3.2.2",
+        "@types/tinycolor2": "^1.4.6",
+        "@vitejs/plugin-vue": "^5.1.0",
+        "@vue/eslint-config-typescript": "^12.0.0",
+        "@vue/tsconfig": "^0.5.0",
+        "eslint": "^8.49.0",
+        "eslint-plugin-vue": "^9.17.0",
+        "husky": "^8.0.3",
+        "npm-run-all2": "^6.1.1",
+        "sass": "^1.69.6",
+        "typescript": "~5.3.0",
+        "vite": "^5.3.5",
+        "vue-tsc": "^2.0.29"
+      }
+    },
+    "node_modules/@aashutoshrathi/word-wrap": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmmirror.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+      "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.23.5",
+      "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.23.5.tgz",
+      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/highlight": "^7.23.4",
+        "chalk": "^2.4.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "dev": true
+    },
+    "node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/code-frame/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.22.20",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight": {
+      "version": "7.23.4",
+      "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.23.4.tgz",
+      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.22.20",
+        "chalk": "^2.4.2",
+        "js-tokens": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "dev": true
+    },
+    "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.24.8",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.24.8.tgz",
+      "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==",
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@commitlint/cli": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/cli/-/cli-18.4.3.tgz",
+      "integrity": "sha512-zop98yfB3A6NveYAZ3P1Mb6bIXuCeWgnUfVNkH4yhIMQpQfzFwseadazOuSn0OOfTt0lWuFauehpm9GcqM5lww==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/format": "^18.4.3",
+        "@commitlint/lint": "^18.4.3",
+        "@commitlint/load": "^18.4.3",
+        "@commitlint/read": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "execa": "^5.0.0",
+        "lodash.isfunction": "^3.0.9",
+        "resolve-from": "5.0.0",
+        "resolve-global": "1.0.0",
+        "yargs": "^17.0.0"
+      },
+      "bin": {
+        "commitlint": "cli.js"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/cli/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@commitlint/config-conventional": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/config-conventional/-/config-conventional-18.4.3.tgz",
+      "integrity": "sha512-729eRRaNta7JZF07qf6SAGSghoDEp9mH7yHU0m7ff0q89W97wDrWCyZ3yoV3mcQJwbhlmVmZPTkPcm7qiAu8WA==",
+      "dev": true,
+      "dependencies": {
+        "conventional-changelog-conventionalcommits": "^7.0.2"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/config-validator": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/config-validator/-/config-validator-18.4.3.tgz",
+      "integrity": "sha512-FPZZmTJBARPCyef9ohRC9EANiQEKSWIdatx5OlgeHKu878dWwpyeFauVkhzuBRJFcCA4Uvz/FDtlDKs008IHcA==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^18.4.3",
+        "ajv": "^8.11.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/config-validator/node_modules/ajv": {
+      "version": "8.12.0",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz",
+      "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true
+    },
+    "node_modules/@commitlint/ensure": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/ensure/-/ensure-18.4.3.tgz",
+      "integrity": "sha512-MI4fwD9TWDVn4plF5+7JUyLLbkOdzIRBmVeNlk4dcGlkrVA+/l5GLcpN66q9LkFsFv6G2X31y89ApA3hqnqIFg==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^18.4.3",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.kebabcase": "^4.1.1",
+        "lodash.snakecase": "^4.1.1",
+        "lodash.startcase": "^4.4.0",
+        "lodash.upperfirst": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/execute-rule": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/execute-rule/-/execute-rule-18.4.3.tgz",
+      "integrity": "sha512-t7FM4c+BdX9WWZCPrrbV5+0SWLgT3kCq7e7/GhHCreYifg3V8qyvO127HF796vyFql75n4TFF+5v1asOOWkV1Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/format": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/format/-/format-18.4.3.tgz",
+      "integrity": "sha512-8b+ItXYHxAhRAXFfYki5PpbuMMOmXYuzLxib65z2XTqki59YDQJGpJ/wB1kEE5MQDgSTQWtKUrA8n9zS/1uIDQ==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^18.4.3",
+        "chalk": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/is-ignored": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/is-ignored/-/is-ignored-18.4.3.tgz",
+      "integrity": "sha512-ZseOY9UfuAI32h9w342Km4AIaTieeFskm2ZKdrG7r31+c6zGBzuny9KQhwI9puc0J3GkUquEgKJblCl7pMnjwg==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^18.4.3",
+        "semver": "7.5.4"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/lint": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/lint/-/lint-18.4.3.tgz",
+      "integrity": "sha512-18u3MRgEXNbnYkMOWoncvq6QB8/90m9TbERKgdPqVvS+zQ/MsuRhdvHYCIXGXZxUb0YI4DV2PC4bPneBV/fYuA==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/is-ignored": "^18.4.3",
+        "@commitlint/parse": "^18.4.3",
+        "@commitlint/rules": "^18.4.3",
+        "@commitlint/types": "^18.4.3"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/load": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/load/-/load-18.4.3.tgz",
+      "integrity": "sha512-v6j2WhvRQJrcJaj5D+EyES2WKTxPpxENmNpNG3Ww8MZGik3jWRXtph0QTzia5ZJyPh2ib5aC/6BIDymkUUM58Q==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/config-validator": "^18.4.3",
+        "@commitlint/execute-rule": "^18.4.3",
+        "@commitlint/resolve-extends": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "@types/node": "^18.11.9",
+        "chalk": "^4.1.0",
+        "cosmiconfig": "^8.3.6",
+        "cosmiconfig-typescript-loader": "^5.0.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "lodash.uniq": "^4.5.0",
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/load/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@commitlint/message": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/message/-/message-18.4.3.tgz",
+      "integrity": "sha512-ddJ7AztWUIoEMAXoewx45lKEYEOeOlBVWjk8hDMUGpprkuvWULpaXczqdjwVtjrKT3JhhN+gMs8pm5G3vB2how==",
+      "dev": true,
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/parse": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/parse/-/parse-18.4.3.tgz",
+      "integrity": "sha512-eoH7CXM9L+/Me96KVcfJ27EIIbA5P9sqw3DqjJhRYuhaULIsPHFs5S5GBDCqT0vKZQDx0DgxhMpW6AQbnKrFtA==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^18.4.3",
+        "conventional-changelog-angular": "^7.0.0",
+        "conventional-commits-parser": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/read": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/read/-/read-18.4.3.tgz",
+      "integrity": "sha512-H4HGxaYA6OBCimZAtghL+B+SWu8ep4X7BwgmedmqWZRHxRLcX2q0bWBtUm5FsMbluxbOfrJwOs/Z0ah4roP/GQ==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/top-level": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "fs-extra": "^11.0.0",
+        "git-raw-commits": "^2.0.11",
+        "minimist": "^1.2.6"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/resolve-extends": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/resolve-extends/-/resolve-extends-18.4.3.tgz",
+      "integrity": "sha512-30sk04LZWf8+SDgJrbJCjM90gTg2LxsD9cykCFeFu+JFHvBFq5ugzp2eO/DJGylAdVaqxej3c7eTSE64hR/lnw==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/config-validator": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "import-fresh": "^3.0.0",
+        "lodash.mergewith": "^4.6.2",
+        "resolve-from": "^5.0.0",
+        "resolve-global": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@commitlint/rules": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/rules/-/rules-18.4.3.tgz",
+      "integrity": "sha512-8KIeukDf45BiY+Lul1T0imSNXF0sMrlLG6JpLLKolkmYVQ6PxxoNOriwyZ3UTFFpaVbPy0rcITaV7U9JCAfDTA==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/ensure": "^18.4.3",
+        "@commitlint/message": "^18.4.3",
+        "@commitlint/to-lines": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "execa": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/to-lines": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/to-lines/-/to-lines-18.4.3.tgz",
+      "integrity": "sha512-fy1TAleik4Zfru1RJ8ZU6cOSvgSVhUellxd3WZV1D5RwHZETt1sZdcA4mQN2y3VcIZsUNKkW0Mq8CM9/L9harQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/top-level": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/top-level/-/top-level-18.4.3.tgz",
+      "integrity": "sha512-E6fJPBLPFL5R8+XUNSYkj4HekIOuGMyJo3mIx2PkYc3clel+pcWQ7TConqXxNWW4x1ugigiIY2RGot55qUq1hw==",
+      "dev": true,
+      "dependencies": {
+        "find-up": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/types": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/types/-/types-18.4.3.tgz",
+      "integrity": "sha512-cvzx+vtY/I2hVBZHCLrpoh+sA0hfuzHwDc+BAFPimYLjJkpHnghQM+z8W/KyLGkygJh3BtI3xXXq+dKjnSWEmA==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.10.0",
+      "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+      "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+      "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.6.0",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "8.56.0",
+      "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.56.0.tgz",
+      "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@humanwhocodes/config-array": {
+      "version": "0.11.13",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
+      "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
+      "dev": true,
+      "dependencies": {
+        "@humanwhocodes/object-schema": "^2.0.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      },
+      "engines": {
+        "node": ">=10.10.0"
+      }
+    },
+    "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.22"
+      }
+    },
+    "node_modules/@humanwhocodes/object-schema": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
+      "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
+      "dev": true
+    },
+    "node_modules/@icon-park/vue-next": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmmirror.com/@icon-park/vue-next/-/vue-next-1.4.2.tgz",
+      "integrity": "sha512-+QklF255wkfBOabY+xw6FAI0Bwln/RhdwCunNy/9sKdKuChtaU67QZqU67KGAvZUTeeBgsL+yaHHxqfQeGZXEQ==",
+      "engines": {
+        "node": ">= 8.0.0",
+        "npm": ">= 5.0.0"
+      },
+      "peerDependencies": {
+        "vue": "3.x"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.8",
+      "resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.8.tgz",
+      "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz",
+      "integrity": "sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.0.tgz",
+      "integrity": "sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.0.tgz",
+      "integrity": "sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.0.tgz",
+      "integrity": "sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.0.tgz",
+      "integrity": "sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.0.tgz",
+      "integrity": "sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.0.tgz",
+      "integrity": "sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.0.tgz",
+      "integrity": "sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.0.tgz",
+      "integrity": "sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.0.tgz",
+      "integrity": "sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.0.tgz",
+      "integrity": "sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.0.tgz",
+      "integrity": "sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.0.tgz",
+      "integrity": "sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.0.tgz",
+      "integrity": "sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.0.tgz",
+      "integrity": "sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.0.tgz",
+      "integrity": "sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rushstack/eslint-patch": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.6.1.tgz",
+      "integrity": "sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==",
+      "dev": true
+    },
+    "node_modules/@tsconfig/node18": {
+      "version": "18.2.2",
+      "resolved": "https://registry.npmmirror.com/@tsconfig/node18/-/node18-18.2.2.tgz",
+      "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==",
+      "dev": true
+    },
+    "node_modules/@types/crypto-js": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.1.tgz",
+      "integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
+      "dev": true
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
+    "node_modules/@types/file-saver": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmmirror.com/@types/file-saver/-/file-saver-2.0.7.tgz",
+      "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
+      "dev": true
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true
+    },
+    "node_modules/@types/lodash": {
+      "version": "4.14.202",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
+      "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
+      "dev": true
+    },
+    "node_modules/@types/minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==",
+      "dev": true
+    },
+    "node_modules/@types/node": {
+      "version": "18.19.4",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.4.tgz",
+      "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==",
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "node_modules/@types/normalize-package-data": {
+      "version": "2.4.4",
+      "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
+      "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
+      "dev": true
+    },
+    "node_modules/@types/semver": {
+      "version": "7.5.6",
+      "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.5.6.tgz",
+      "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
+      "dev": true
+    },
+    "node_modules/@types/svg-arc-to-cubic-bezier": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/@types/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.2.tgz",
+      "integrity": "sha512-XQtKy9lmkKlV+c3Jelo7kxNPw7qOqIq3GcnOhywGZHF7zw5D5m+Ssigbmf3Turbe/A8Ur+lRh8TYjuxXKvyivw==",
+      "dev": true
+    },
+    "node_modules/@types/tinycolor2": {
+      "version": "1.4.6",
+      "resolved": "https://registry.npmmirror.com/@types/tinycolor2/-/tinycolor2-1.4.6.tgz",
+      "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==",
+      "dev": true
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.16.0.tgz",
+      "integrity": "sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.5.1",
+        "@typescript-eslint/scope-manager": "6.16.0",
+        "@typescript-eslint/type-utils": "6.16.0",
+        "@typescript-eslint/utils": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0",
+        "debug": "^4.3.4",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.4",
+        "natural-compare": "^1.4.0",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-6.16.0.tgz",
+      "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "6.16.0",
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/typescript-estree": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz",
+      "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-6.16.0.tgz",
+      "integrity": "sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/typescript-estree": "6.16.0",
+        "@typescript-eslint/utils": "6.16.0",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-6.16.0.tgz",
+      "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==",
+      "dev": true,
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz",
+      "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "minimatch": "9.0.3",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-6.16.0.tgz",
+      "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "@types/json-schema": "^7.0.12",
+        "@types/semver": "^7.5.0",
+        "@typescript-eslint/scope-manager": "6.16.0",
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/typescript-estree": "6.16.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz",
+      "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "6.16.0",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@ungap/structured-clone": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+      "dev": true
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.1.0.tgz",
+      "integrity": "sha512-QMRxARyrdiwi1mj3AW4fLByoHTavreXq0itdEW696EihXglf1MB3D4C2gBvE0jMPH29ZjC3iK8aIaUMLf4EOGA==",
+      "dev": true,
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@volar/language-core": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==",
+      "dev": true,
+      "dependencies": {
+        "@volar/source-map": "2.4.0-alpha.18"
+      }
+    },
+    "node_modules/@volar/source-map": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g==",
+      "dev": true
+    },
+    "node_modules/@volar/typescript": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==",
+      "dev": true,
+      "dependencies": {
+        "@volar/language-core": "2.4.0-alpha.18",
+        "path-browserify": "^1.0.1",
+        "vscode-uri": "^3.0.8"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.34.tgz",
+      "integrity": "sha512-Z0izUf32+wAnQewjHu+pQf1yw00EGOmevl1kE+ljjjMe7oEfpQ+BI3/JNK7yMB4IrUsqLDmPecUrpj3mCP+yJQ==",
+      "dependencies": {
+        "@babel/parser": "^7.24.7",
+        "@vue/shared": "3.4.34",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.34.tgz",
+      "integrity": "sha512-3PUOTS1h5cskdOJMExCu2TInXuM0j60DRPpSCJDqOCupCfUZCJoyQmKtRmA8EgDNZ5kcEE7vketamRZfrEuVDw==",
+      "dependencies": {
+        "@vue/compiler-core": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.34.tgz",
+      "integrity": "sha512-x6lm0UrM03jjDXTPZgD9Ad8bIVD1ifWNit2EaWQIZB5CULr46+FbLQ5RpK7AXtDHGjx9rmvC7QRCTjsiGkAwRw==",
+      "dependencies": {
+        "@babel/parser": "^7.24.7",
+        "@vue/compiler-core": "3.4.34",
+        "@vue/compiler-dom": "3.4.34",
+        "@vue/compiler-ssr": "3.4.34",
+        "@vue/shared": "3.4.34",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.10",
+        "postcss": "^8.4.39",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.34.tgz",
+      "integrity": "sha512-8TDBcLaTrFm5rnF+Qm4BlliaopJgqJ28Nsrc80qazynm5aJO+Emu7y0RWw34L8dNnTRdcVBpWzJxhGYzsoVu4g==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "node_modules/@vue/compiler-vue2": {
+      "version": "2.7.16",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
+      "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
+      "dev": true,
+      "dependencies": {
+        "de-indent": "^1.0.2",
+        "he": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
+      "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
+    },
+    "node_modules/@vue/eslint-config-typescript": {
+      "version": "12.0.0",
+      "resolved": "https://registry.npmmirror.com/@vue/eslint-config-typescript/-/eslint-config-typescript-12.0.0.tgz",
+      "integrity": "sha512-StxLFet2Qe97T8+7L8pGlhYBBr8Eg05LPuTDVopQV6il+SK6qqom59BA/rcFipUef2jD8P2X44Vd8tMFytfvlg==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "^6.7.0",
+        "@typescript-eslint/parser": "^6.7.0",
+        "vue-eslint-parser": "^9.3.1"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0",
+        "eslint-plugin-vue": "^9.0.0",
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/language-core": {
+      "version": "2.0.29",
+      "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.0.29.tgz",
+      "integrity": "sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==",
+      "dev": true,
+      "dependencies": {
+        "@volar/language-core": "~2.4.0-alpha.18",
+        "@vue/compiler-dom": "^3.4.0",
+        "@vue/compiler-vue2": "^2.7.16",
+        "@vue/shared": "^3.4.0",
+        "computeds": "^0.0.1",
+        "minimatch": "^9.0.3",
+        "muggle-string": "^0.4.1",
+        "path-browserify": "^1.0.1"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.34.tgz",
+      "integrity": "sha512-ua+Lo+wBRlBEX9TtgPOShE2JwIO7p6BTZ7t1KZVPoaBRfqbC7N3c8Mpzicx173fXxx5VXeU6ykiHo7WgLzJQDA==",
+      "dependencies": {
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.34.tgz",
+      "integrity": "sha512-PXhkiRPwcPGJ1BnyBZFI96GfInCVskd0HPNIAZn7i3YOmLbtbTZpB7/kDTwC1W7IqdGPkTVC63IS7J2nZs4Ebg==",
+      "dependencies": {
+        "@vue/reactivity": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.34.tgz",
+      "integrity": "sha512-dXqIe+RqFAK2Euak4UsvbIupalrhc67OuQKpD7HJ3W2fv8jlqvI7szfBCsAEcE8o/wyNpkloxB6J8viuF/E3gw==",
+      "dependencies": {
+        "@vue/reactivity": "3.4.34",
+        "@vue/runtime-core": "3.4.34",
+        "@vue/shared": "3.4.34",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.34.tgz",
+      "integrity": "sha512-GeyEUfMVRZMD/mZcNONEqg7MiU10QQ1DB3O/Qr6+8uXpbwdlmVgQ5Qs1/ZUAFX1X2UUtqMoGrDRbxdWfOJFT7Q==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.4.34",
+        "@vue/shared": "3.4.34"
+      },
+      "peerDependencies": {
+        "vue": "3.4.34"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.34.tgz",
+      "integrity": "sha512-x5LmiRLpRsd9KTjAB8MPKf0CDPMcuItjP0gbNqFCIgL1I8iYp4zglhj9w9FPCdIbHG2M91RVeIbArFfFTz9I3A=="
+    },
+    "node_modules/@vue/tsconfig": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.5.1.tgz",
+      "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
+      "dev": true
+    },
+    "node_modules/acorn": {
+      "version": "8.11.3",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
+      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "node_modules/animate.css": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz",
+      "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
+    "node_modules/array-ify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/array-ify/-/array-ify-1.0.0.tgz",
+      "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==",
+      "dev": true
+    },
+    "node_modules/array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true
+    },
+    "node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camelcase-keys": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
+      "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
+      "dev": true,
+      "dependencies": {
+        "camelcase": "^5.3.1",
+        "map-obj": "^4.0.0",
+        "quick-lru": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/chokidar/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/clipboard": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz",
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+      "dependencies": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/compare-func": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/compare-func/-/compare-func-2.0.0.tgz",
+      "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+      "dev": true,
+      "dependencies": {
+        "array-ify": "^1.0.0",
+        "dot-prop": "^5.1.0"
+      }
+    },
+    "node_modules/computeds": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/computeds/-/computeds-0.0.1.tgz",
+      "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==",
+      "dev": true
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
+    },
+    "node_modules/conventional-changelog-angular": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+      "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+      "dev": true,
+      "dependencies": {
+        "compare-func": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/conventional-changelog-conventionalcommits": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmmirror.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz",
+      "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==",
+      "dev": true,
+      "dependencies": {
+        "compare-func": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/conventional-commits-parser": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+      "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+      "dev": true,
+      "dependencies": {
+        "is-text-path": "^2.0.0",
+        "JSONStream": "^1.3.5",
+        "meow": "^12.0.1",
+        "split2": "^4.0.0"
+      },
+      "bin": {
+        "conventional-commits-parser": "cli.mjs"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
+    "node_modules/cosmiconfig": {
+      "version": "8.3.6",
+      "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+      "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+      "dev": true,
+      "dependencies": {
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0",
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/cosmiconfig-typescript-loader": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz",
+      "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==",
+      "dev": true,
+      "dependencies": {
+        "jiti": "^1.19.1"
+      },
+      "engines": {
+        "node": ">=v16"
+      },
+      "peerDependencies": {
+        "@types/node": "*",
+        "cosmiconfig": ">=8.2",
+        "typescript": ">=4"
+      }
+    },
+    "node_modules/cosmiconfig/node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true
+    },
+    "node_modules/cosmiconfig/node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "node_modules/cosmiconfig/node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true,
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
+    "node_modules/dargs": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/dargs/-/dargs-7.0.0.tgz",
+      "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/de-indent": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
+      "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+      "dev": true
+    },
+    "node_modules/debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/decamelize-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz",
+      "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==",
+      "dev": true,
+      "dependencies": {
+        "decamelize": "^1.1.0",
+        "map-obj": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/decamelize-keys/node_modules/map-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-1.0.1.tgz",
+      "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
+    },
+    "node_modules/delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
+    "node_modules/dexie": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/dexie/-/dexie-3.0.3.tgz",
+      "integrity": "sha512-BSFhGpngnCl1DOr+8YNwBDobRMH0ziJs2vts69VilwetHYOtEDcLqo7d/XiIphM0tJZ2rPPyAGd31lgH2Ln3nw==",
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "dependencies": {
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/dot-prop": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-5.3.0.tgz",
+      "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+      "dev": true,
+      "dependencies": {
+        "is-obj": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/echarts": {
+      "version": "5.5.1",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz",
+      "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==",
+      "dependencies": {
+        "tslib": "2.3.0",
+        "zrender": "5.6.0"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "8.56.0",
+      "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.56.0.tgz",
+      "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.6.1",
+        "@eslint/eslintrc": "^2.1.4",
+        "@eslint/js": "8.56.0",
+        "@humanwhocodes/config-array": "^0.11.13",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "@ungap/structured-clone": "^1.2.0",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.2.2",
+        "eslint-visitor-keys": "^3.4.3",
+        "espree": "^9.6.1",
+        "esquery": "^1.4.2",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3",
+        "strip-ansi": "^6.0.1",
+        "text-table": "^0.2.0"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-vue": {
+      "version": "9.19.2",
+      "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.19.2.tgz",
+      "integrity": "sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "natural-compare": "^1.4.0",
+        "nth-check": "^2.1.1",
+        "postcss-selector-parser": "^6.0.13",
+        "semver": "^7.5.4",
+        "vue-eslint-parser": "^9.3.1",
+        "xml-name-validator": "^4.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "7.2.2",
+      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz",
+      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+      "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/eslint/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.5.0.tgz",
+      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz",
+      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true
+    },
+    "node_modules/fastq": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.16.0.tgz",
+      "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
+      "dev": true,
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "dependencies": {
+        "flat-cache": "^3.0.4"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz",
+      "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+      "dev": true,
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.3",
+        "rimraf": "^3.0.2"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.2.9",
+      "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.2.9.tgz",
+      "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+      "dev": true
+    },
+    "node_modules/fs-extra": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz",
+      "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/git-raw-commits": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmmirror.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz",
+      "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==",
+      "dev": true,
+      "dependencies": {
+        "dargs": "^7.0.0",
+        "lodash": "^4.17.15",
+        "meow": "^8.0.0",
+        "split2": "^3.0.0",
+        "through2": "^4.0.0"
+      },
+      "bin": {
+        "git-raw-commits": "cli.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/hosted-git-info": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+      "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/meow": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmmirror.com/meow/-/meow-8.1.2.tgz",
+      "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==",
+      "dev": true,
+      "dependencies": {
+        "@types/minimist": "^1.2.0",
+        "camelcase-keys": "^6.2.2",
+        "decamelize-keys": "^1.1.0",
+        "hard-rejection": "^2.1.0",
+        "minimist-options": "4.1.0",
+        "normalize-package-data": "^3.0.0",
+        "read-pkg-up": "^7.0.1",
+        "redent": "^3.0.0",
+        "trim-newlines": "^3.0.0",
+        "type-fest": "^0.18.0",
+        "yargs-parser": "^20.2.3"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/normalize-package-data": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
+      "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==",
+      "dev": true,
+      "dependencies": {
+        "hosted-git-info": "^4.0.1",
+        "is-core-module": "^2.5.0",
+        "semver": "^7.3.4",
+        "validate-npm-package-license": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dev": true,
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/split2": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/split2/-/split2-3.2.2.tgz",
+      "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+      "dev": true,
+      "dependencies": {
+        "readable-stream": "^3.0.0"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/through2": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/through2/-/through2-4.0.2.tgz",
+      "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+      "dev": true,
+      "dependencies": {
+        "readable-stream": "3"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/type-fest": {
+      "version": "0.18.1",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.18.1.tgz",
+      "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/glob/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/glob/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/global-dirs": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmmirror.com/global-dirs/-/global-dirs-0.1.1.tgz",
+      "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==",
+      "dev": true,
+      "dependencies": {
+        "ini": "^1.3.4"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/globals": {
+      "version": "13.24.0",
+      "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "dependencies": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+      "dependencies": {
+        "delegate": "^3.1.2"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "node_modules/graphemer": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz",
+      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+      "dev": true
+    },
+    "node_modules/hard-rejection": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/hard-rejection/-/hard-rejection-2.1.0.tgz",
+      "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz",
+      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true,
+      "bin": {
+        "he": "bin/he"
+      }
+    },
+    "node_modules/hfmath": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/hfmath/-/hfmath-0.0.2.tgz",
+      "integrity": "sha512-cKUi0yiQLGfLgs8+3Iw5nAiqSH13Knp7vCf0G1vlF5nfiKKO1XmxNagMvyp0F4ZvUNaHpRGTkmc7nowCy1S58g=="
+    },
+    "node_modules/hosted-git-info": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-7.0.1.tgz",
+      "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^10.0.1"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/html-to-image": {
+      "version": "1.11.11",
+      "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz",
+      "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
+    },
+    "node_modules/https": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/https/-/https-1.0.0.tgz",
+      "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="
+    },
+    "node_modules/human-signals": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10.17.0"
+      }
+    },
+    "node_modules/husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmmirror.com/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true,
+      "bin": {
+        "husky": "lib/bin.js"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/ignore": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.0.tgz",
+      "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/image-size": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/image-size/-/image-size-1.1.0.tgz",
+      "integrity": "sha512-asnTHw2K8OlqT5kVnQwX+AGKQqpvLo95LbNzQ/C0ln3yzentZmAdd0ygoD004VC4Kkd4PV7J2iaPQkqwp9yuTw==",
+      "dependencies": {
+        "queue": "6.0.2"
+      },
+      "bin": {
+        "image-size": "bin/image-size.js"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
+    "node_modules/immutable": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.4.tgz",
+      "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
+      "dev": true
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "dev": true
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.13.1",
+      "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz",
+      "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+      "dev": true,
+      "dependencies": {
+        "hasown": "^2.0.0"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-obj": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz",
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+      "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-text-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/is-text-path/-/is-text-path-2.0.0.tgz",
+      "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+      "dev": true,
+      "dependencies": {
+        "text-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true
+    },
+    "node_modules/jiti": {
+      "version": "1.21.0",
+      "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.0.tgz",
+      "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+      "dev": true,
+      "bin": {
+        "jiti": "bin/jiti.js"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz",
+      "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==",
+      "dev": true,
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true
+    },
+    "node_modules/jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dev": true,
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+      "dev": true,
+      "engines": [
+        "node >= 0.2.0"
+      ]
+    },
+    "node_modules/JSONStream": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmmirror.com/JSONStream/-/JSONStream-1.3.5.tgz",
+      "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+      "dev": true,
+      "dependencies": {
+        "jsonparse": "^1.2.0",
+        "through": ">=2.2.7 <3"
+      },
+      "bin": {
+        "JSONStream": "bin.js"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "dependencies": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz",
+      "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==",
+      "dev": true,
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "dev": true
+    },
+    "node_modules/lodash.isfunction": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmmirror.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+      "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
+      "dev": true
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "dev": true
+    },
+    "node_modules/lodash.kebabcase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+      "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+      "dev": true
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "node_modules/lodash.mergewith": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+      "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+      "dev": true
+    },
+    "node_modules/lodash.snakecase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+      "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+      "dev": true
+    },
+    "node_modules/lodash.startcase": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmmirror.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+      "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+      "dev": true
+    },
+    "node_modules/lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+      "dev": true
+    },
+    "node_modules/lodash.upperfirst": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+      "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+      "dev": true
+    },
+    "node_modules/lru-cache": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.1.0.tgz",
+      "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==",
+      "dev": true,
+      "engines": {
+        "node": "14 || >=16.14"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.10",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz",
+      "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      }
+    },
+    "node_modules/map-obj": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-4.3.0.tgz",
+      "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmmirror.com/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/meow": {
+      "version": "12.1.1",
+      "resolved": "https://registry.npmmirror.com/meow/-/meow-12.1.1.tgz",
+      "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.10"
+      }
+    },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dev": true,
+      "dependencies": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/min-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz",
+      "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz",
+      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true
+    },
+    "node_modules/minimist-options": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz",
+      "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==",
+      "dev": true,
+      "dependencies": {
+        "arrify": "^1.0.1",
+        "is-plain-obj": "^1.1.0",
+        "kind-of": "^6.0.3"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
+    },
+    "node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "node_modules/muggle-string": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz",
+      "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+      "dev": true
+    },
+    "node_modules/nanoid": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.0.7.tgz",
+      "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.js"
+      },
+      "engines": {
+        "node": "^18 || >=20"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true
+    },
+    "node_modules/normalize-package-data": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-6.0.0.tgz",
+      "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==",
+      "dev": true,
+      "dependencies": {
+        "hosted-git-info": "^7.0.0",
+        "is-core-module": "^2.8.1",
+        "semver": "^7.3.5",
+        "validate-npm-package-license": "^3.0.4"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npm-run-all2": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmmirror.com/npm-run-all2/-/npm-run-all2-6.1.1.tgz",
+      "integrity": "sha512-lWLbkPZ5BSdXtN8lR+0rc8caKoPdymycpZksyDEC9MOBvfdwTXZ0uVhb7bMcGeXv2/BKtfQuo6Zn3zfc8rxNXA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "cross-spawn": "^7.0.3",
+        "memorystream": "^0.3.1",
+        "minimatch": "^9.0.0",
+        "pidtree": "^0.6.0",
+        "read-pkg": "^8.0.0",
+        "shell-quote": "^1.7.3"
+      },
+      "bin": {
+        "npm-run-all": "bin/npm-run-all/index.js",
+        "npm-run-all2": "bin/npm-run-all/index.js",
+        "run-p": "bin/run-p/index.js",
+        "run-s": "bin/run-s/index.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0",
+        "npm": ">= 8"
+      }
+    },
+    "node_modules/npm-run-all2/node_modules/ansi-styles": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz",
+      "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      }
+    },
+    "node_modules/number-precision": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz",
+      "integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ=="
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz",
+      "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+      "dev": true,
+      "dependencies": {
+        "@aashutoshrathi/word-wrap": "^1.2.3",
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/orderedmap": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/orderedmap/-/orderedmap-2.1.1.tgz",
+      "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-7.1.1.tgz",
+      "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.21.4",
+        "error-ex": "^1.3.2",
+        "json-parse-even-better-errors": "^3.0.0",
+        "lines-and-columns": "^2.0.3",
+        "type-fest": "^3.8.0"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/parse-json/node_modules/type-fest": {
+      "version": "3.13.1",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz",
+      "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.16"
+      }
+    },
+    "node_modules/path-browserify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+      "dev": true
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz",
+      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/pidtree": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz",
+      "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+      "dev": true,
+      "bin": {
+        "pidtree": "bin/pidtree.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz",
+      "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.5.0",
+        "vue-demi": ">=0.14.5"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.4.0",
+        "typescript": ">=4.4.4",
+        "vue": "^2.6.14 || ^3.3.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia/node_modules/vue-demi": {
+      "version": "0.14.6",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz",
+      "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.4.39",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.39.tgz",
+      "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.1",
+        "source-map-js": "^1.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "6.0.15",
+      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
+      "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
+      "dev": true,
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss/node_modules/nanoid": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/pptxgenjs": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmmirror.com/pptxgenjs/-/pptxgenjs-3.12.0.tgz",
+      "integrity": "sha512-ZozkYKWb1MoPR4ucw3/aFYlHkVIJxo9czikEclcUVnS4Iw/M+r+TEwdlB3fyAWO9JY1USxJDt0Y0/r15IR/RUA==",
+      "dependencies": {
+        "@types/node": "^18.7.3",
+        "https": "^1.0.0",
+        "image-size": "^1.0.0",
+        "jszip": "^3.7.1"
+      }
+    },
+    "node_modules/pptxtojson": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/pptxtojson/-/pptxtojson-1.0.3.tgz",
+      "integrity": "sha512-Q7tvtqFUm9x4CGRv/BUt69yeJS1RtIKBuh/G/gpxdtSbm7zAVeTPbPRMS+1hfliVXhzYJDgbg4zc8F4hwmq71A==",
+      "dependencies": {
+        "jszip": "^3.10.1",
+        "tinycolor2": "1.6.0",
+        "txml": "^5.1.1"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
+    "node_modules/prosemirror-commands": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/prosemirror-commands/-/prosemirror-commands-1.6.0.tgz",
+      "integrity": "sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==",
+      "dependencies": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-dropcursor": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz",
+      "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==",
+      "dependencies": {
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.1.0",
+        "prosemirror-view": "^1.1.0"
+      }
+    },
+    "node_modules/prosemirror-gapcursor": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz",
+      "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==",
+      "dependencies": {
+        "prosemirror-keymap": "^1.0.0",
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-view": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-history": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-history/-/prosemirror-history-1.3.2.tgz",
+      "integrity": "sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==",
+      "dependencies": {
+        "prosemirror-state": "^1.2.2",
+        "prosemirror-transform": "^1.0.0",
+        "prosemirror-view": "^1.31.0",
+        "rope-sequence": "^1.3.0"
+      }
+    },
+    "node_modules/prosemirror-inputrules": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz",
+      "integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==",
+      "dependencies": {
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-keymap": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz",
+      "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==",
+      "dependencies": {
+        "prosemirror-state": "^1.0.0",
+        "w3c-keyname": "^2.2.0"
+      }
+    },
+    "node_modules/prosemirror-model": {
+      "version": "1.22.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-model/-/prosemirror-model-1.22.2.tgz",
+      "integrity": "sha512-I4lS7HHIW47D0Xv/gWmi4iUWcQIDYaJKd8Hk4+lcSps+553FlQrhmxtItpEvTr75iAruhzVShVp6WUwsT6Boww==",
+      "dependencies": {
+        "orderedmap": "^2.0.0"
+      }
+    },
+    "node_modules/prosemirror-schema-basic": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz",
+      "integrity": "sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==",
+      "dependencies": {
+        "prosemirror-model": "^1.19.0"
+      }
+    },
+    "node_modules/prosemirror-schema-list": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/prosemirror-schema-list/-/prosemirror-schema-list-1.4.1.tgz",
+      "integrity": "sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==",
+      "dependencies": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.7.3"
+      }
+    },
+    "node_modules/prosemirror-state": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmmirror.com/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
+      "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
+      "dependencies": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-transform": "^1.0.0",
+        "prosemirror-view": "^1.27.0"
+      }
+    },
+    "node_modules/prosemirror-transform": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/prosemirror-transform/-/prosemirror-transform-1.8.0.tgz",
+      "integrity": "sha512-BaSBsIMv52F1BVVMvOmp1yzD3u65uC3HTzCBQV1WDPqJRQ2LuHKcyfn0jwqodo8sR9vVzMzZyI+Dal5W9E6a9A==",
+      "dependencies": {
+        "prosemirror-model": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-view": {
+      "version": "1.33.9",
+      "resolved": "https://registry.npmmirror.com/prosemirror-view/-/prosemirror-view-1.33.9.tgz",
+      "integrity": "sha512-xV1A0Vz9cIcEnwmMhKKFAOkfIp8XmJRnaZoPqNXrPS7EK5n11Ov8V76KhR0RsfQd/SIzmWY+bg+M44A2Lx/Nnw==",
+      "dependencies": {
+        "prosemirror-model": "^1.20.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.1.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/queue": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/queue/-/queue-6.0.2.tgz",
+      "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
+      "dependencies": {
+        "inherits": "~2.0.3"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true
+    },
+    "node_modules/quick-lru": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-4.0.1.tgz",
+      "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-8.1.0.tgz",
+      "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/normalize-package-data": "^2.4.1",
+        "normalize-package-data": "^6.0.0",
+        "parse-json": "^7.0.0",
+        "type-fest": "^4.2.0"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/read-pkg-up": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+      "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+      "dev": true,
+      "dependencies": {
+        "find-up": "^4.1.0",
+        "read-pkg": "^5.2.0",
+        "type-fest": "^0.8.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+      "dev": true
+    },
+    "node_modules/read-pkg-up/node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true
+    },
+    "node_modules/read-pkg-up/node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "node_modules/read-pkg-up/node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "dependencies": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/read-pkg": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz",
+      "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+      "dev": true,
+      "dependencies": {
+        "@types/normalize-package-data": "^2.4.0",
+        "normalize-package-data": "^2.5.0",
+        "parse-json": "^5.0.0",
+        "type-fest": "^0.6.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz",
+      "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/read-pkg-up/node_modules/type-fest": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz",
+      "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/read-pkg/node_modules/type-fest": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-4.9.0.tgz",
+      "integrity": "sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==",
+      "dev": true,
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/redent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz",
+      "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+      "dev": true,
+      "dependencies": {
+        "indent-string": "^4.0.0",
+        "strip-indent": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.8",
+      "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
+      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+      "dev": true,
+      "dependencies": {
+        "is-core-module": "^2.13.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/resolve-global": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-global/-/resolve-global-1.0.0.tgz",
+      "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==",
+      "dev": true,
+      "dependencies": {
+        "global-dirs": "^0.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true,
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.19.0.tgz",
+      "integrity": "sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.5"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.19.0",
+        "@rollup/rollup-android-arm64": "4.19.0",
+        "@rollup/rollup-darwin-arm64": "4.19.0",
+        "@rollup/rollup-darwin-x64": "4.19.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.19.0",
+        "@rollup/rollup-linux-arm-musleabihf": "4.19.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.19.0",
+        "@rollup/rollup-linux-arm64-musl": "4.19.0",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.19.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.19.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.19.0",
+        "@rollup/rollup-linux-x64-gnu": "4.19.0",
+        "@rollup/rollup-linux-x64-musl": "4.19.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.19.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.19.0",
+        "@rollup/rollup-win32-x64-msvc": "4.19.0",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/rope-sequence": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmmirror.com/rope-sequence/-/rope-sequence-1.3.4.tgz",
+      "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/sass": {
+      "version": "1.69.6",
+      "resolved": "https://registry.npmmirror.com/sass/-/sass-1.69.6.tgz",
+      "integrity": "sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+    },
+    "node_modules/semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/semver/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shell-quote": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.1.tgz",
+      "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+      "dev": true
+    },
+    "node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true
+    },
+    "node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/sortablejs": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
+      "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz",
+      "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/spdx-correct": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz",
+      "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+      "dev": true,
+      "dependencies": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "dev": true
+    },
+    "node_modules/spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-license-ids": {
+      "version": "3.0.16",
+      "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
+      "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==",
+      "dev": true
+    },
+    "node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/strip-indent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz",
+      "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+      "dev": true,
+      "dependencies": {
+        "min-indent": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/svg-arc-to-cubic-bezier": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
+      "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="
+    },
+    "node_modules/svg-pathdata": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-7.1.0.tgz",
+      "integrity": "sha512-wrvKHXZSYZyODOj5E1l1bMTIo8sR7YCH0E4SA8IgLgMsZq4RypslpYvNSsrdg4ThD6du2KWPyVeKinkqUelGhg==",
+      "dependencies": {
+        "yerror": "^8.0.0"
+      },
+      "engines": {
+        "node": ">=20.11.1"
+      }
+    },
+    "node_modules/text-extensions": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmmirror.com/text-extensions/-/text-extensions-2.4.0.tgz",
+      "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true
+    },
+    "node_modules/through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+      "dev": true
+    },
+    "node_modules/through2": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/through2/-/through2-3.0.2.tgz",
+      "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
+      "dependencies": {
+        "inherits": "^2.0.4",
+        "readable-stream": "2 || 3"
+      }
+    },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
+    "node_modules/tinycolor2": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz",
+      "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
+    },
+    "node_modules/tippy.js": {
+      "version": "6.3.7",
+      "resolved": "https://registry.npmmirror.com/tippy.js/-/tippy.js-6.3.7.tgz",
+      "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
+      "dependencies": {
+        "@popperjs/core": "^2.9.0"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/trim-newlines": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz",
+      "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ts-api-utils": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
+      "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.13.0"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.2.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+    },
+    "node_modules/txml": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/txml/-/txml-5.1.1.tgz",
+      "integrity": "sha512-TwMDLnXQ09enNaxybLVvKZU7rqog8LgnuAs4ZYXM0nV0eu10iLsSFwlX3AEknAXXtH1wT3CYfoiXAjyBexcmuw==",
+      "dependencies": {
+        "through2": "^3.0.1"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.3.3.tgz",
+      "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+      "devOptional": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+    },
+    "node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
+    "node_modules/validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "dependencies": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "node_modules/vite": {
+      "version": "5.3.5",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-5.3.5.tgz",
+      "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.39",
+        "rollup": "^4.13.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vscode-uri": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.0.8.tgz",
+      "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==",
+      "dev": true
+    },
+    "node_modules/vue": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.34.tgz",
+      "integrity": "sha512-VZze05HWlA3ItreQ/ka7Sx7PoD0/3St8FEiSlSTVgb6l4hL+RjtP2/8g5WQBzZgyf8WG2f+g1bXzC7zggLhAJA==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.4.34",
+        "@vue/compiler-sfc": "3.4.34",
+        "@vue/runtime-dom": "3.4.34",
+        "@vue/server-renderer": "3.4.34",
+        "@vue/shared": "3.4.34"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-eslint-parser": {
+      "version": "9.3.2",
+      "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
+      "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.3.4",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.3.1",
+        "esquery": "^1.4.0",
+        "lodash": "^4.17.21",
+        "semver": "^7.3.6"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": ">=6.0.0"
+      }
+    },
+    "node_modules/vue-tsc": {
+      "version": "2.0.29",
+      "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.0.29.tgz",
+      "integrity": "sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==",
+      "dev": true,
+      "dependencies": {
+        "@volar/typescript": "~2.4.0-alpha.18",
+        "@vue/language-core": "2.0.29",
+        "semver": "^7.5.4"
+      },
+      "bin": {
+        "vue-tsc": "bin/vue-tsc.js"
+      },
+      "peerDependencies": {
+        "typescript": ">=5.0.0"
+      }
+    },
+    "node_modules/vuedraggable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
+      "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+      "dependencies": {
+        "sortablejs": "1.14.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.1"
+      }
+    },
+    "node_modules/w3c-keyname": {
+      "version": "2.2.8",
+      "resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+      "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
+    },
+    "node_modules/xml-name-validator": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yerror": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/yerror/-/yerror-8.0.0.tgz",
+      "integrity": "sha512-FemWD5/UqNm8ffj8oZIbjWXIF2KE0mZssggYpdaQkWDDgXBQ/35PNIxEuz6/YLn9o0kOxDBNJe8x8k9ljD7k/g==",
+      "engines": {
+        "node": ">=18.16.0"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/zrender": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz",
+      "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==",
+      "dependencies": {
+        "tslib": "2.3.0"
+      }
+    }
+  },
+  "dependencies": {
+    "@aashutoshrathi/word-wrap": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmmirror.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+      "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+      "dev": true
+    },
+    "@babel/code-frame": {
+      "version": "7.23.5",
+      "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.23.5.tgz",
+      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.23.4",
+        "chalk": "^2.4.2"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "color-convert": {
+          "version": "1.9.3",
+          "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
+          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+          "dev": true,
+          "requires": {
+            "color-name": "1.1.3"
+          }
+        },
+        "color-name": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+          "dev": true
+        },
+        "escape-string-regexp": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+          "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.22.20",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+      "dev": true
+    },
+    "@babel/highlight": {
+      "version": "7.23.4",
+      "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.23.4.tgz",
+      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.22.20",
+        "chalk": "^2.4.2",
+        "js-tokens": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "color-convert": {
+          "version": "1.9.3",
+          "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
+          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+          "dev": true,
+          "requires": {
+            "color-name": "1.1.3"
+          }
+        },
+        "color-name": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+          "dev": true
+        },
+        "escape-string-regexp": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+          "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "@babel/parser": {
+      "version": "7.24.8",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.24.8.tgz",
+      "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w=="
+    },
+    "@commitlint/cli": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/cli/-/cli-18.4.3.tgz",
+      "integrity": "sha512-zop98yfB3A6NveYAZ3P1Mb6bIXuCeWgnUfVNkH4yhIMQpQfzFwseadazOuSn0OOfTt0lWuFauehpm9GcqM5lww==",
+      "dev": true,
+      "requires": {
+        "@commitlint/format": "^18.4.3",
+        "@commitlint/lint": "^18.4.3",
+        "@commitlint/load": "^18.4.3",
+        "@commitlint/read": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "execa": "^5.0.0",
+        "lodash.isfunction": "^3.0.9",
+        "resolve-from": "5.0.0",
+        "resolve-global": "1.0.0",
+        "yargs": "^17.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/config-conventional": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/config-conventional/-/config-conventional-18.4.3.tgz",
+      "integrity": "sha512-729eRRaNta7JZF07qf6SAGSghoDEp9mH7yHU0m7ff0q89W97wDrWCyZ3yoV3mcQJwbhlmVmZPTkPcm7qiAu8WA==",
+      "dev": true,
+      "requires": {
+        "conventional-changelog-conventionalcommits": "^7.0.2"
+      }
+    },
+    "@commitlint/config-validator": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/config-validator/-/config-validator-18.4.3.tgz",
+      "integrity": "sha512-FPZZmTJBARPCyef9ohRC9EANiQEKSWIdatx5OlgeHKu878dWwpyeFauVkhzuBRJFcCA4Uvz/FDtlDKs008IHcA==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^18.4.3",
+        "ajv": "^8.11.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "8.12.0",
+          "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz",
+          "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/ensure": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/ensure/-/ensure-18.4.3.tgz",
+      "integrity": "sha512-MI4fwD9TWDVn4plF5+7JUyLLbkOdzIRBmVeNlk4dcGlkrVA+/l5GLcpN66q9LkFsFv6G2X31y89ApA3hqnqIFg==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^18.4.3",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.kebabcase": "^4.1.1",
+        "lodash.snakecase": "^4.1.1",
+        "lodash.startcase": "^4.4.0",
+        "lodash.upperfirst": "^4.3.1"
+      }
+    },
+    "@commitlint/execute-rule": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/execute-rule/-/execute-rule-18.4.3.tgz",
+      "integrity": "sha512-t7FM4c+BdX9WWZCPrrbV5+0SWLgT3kCq7e7/GhHCreYifg3V8qyvO127HF796vyFql75n4TFF+5v1asOOWkV1Q==",
+      "dev": true
+    },
+    "@commitlint/format": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/format/-/format-18.4.3.tgz",
+      "integrity": "sha512-8b+ItXYHxAhRAXFfYki5PpbuMMOmXYuzLxib65z2XTqki59YDQJGpJ/wB1kEE5MQDgSTQWtKUrA8n9zS/1uIDQ==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^18.4.3",
+        "chalk": "^4.1.0"
+      }
+    },
+    "@commitlint/is-ignored": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/is-ignored/-/is-ignored-18.4.3.tgz",
+      "integrity": "sha512-ZseOY9UfuAI32h9w342Km4AIaTieeFskm2ZKdrG7r31+c6zGBzuny9KQhwI9puc0J3GkUquEgKJblCl7pMnjwg==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^18.4.3",
+        "semver": "7.5.4"
+      }
+    },
+    "@commitlint/lint": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/lint/-/lint-18.4.3.tgz",
+      "integrity": "sha512-18u3MRgEXNbnYkMOWoncvq6QB8/90m9TbERKgdPqVvS+zQ/MsuRhdvHYCIXGXZxUb0YI4DV2PC4bPneBV/fYuA==",
+      "dev": true,
+      "requires": {
+        "@commitlint/is-ignored": "^18.4.3",
+        "@commitlint/parse": "^18.4.3",
+        "@commitlint/rules": "^18.4.3",
+        "@commitlint/types": "^18.4.3"
+      }
+    },
+    "@commitlint/load": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/load/-/load-18.4.3.tgz",
+      "integrity": "sha512-v6j2WhvRQJrcJaj5D+EyES2WKTxPpxENmNpNG3Ww8MZGik3jWRXtph0QTzia5ZJyPh2ib5aC/6BIDymkUUM58Q==",
+      "dev": true,
+      "requires": {
+        "@commitlint/config-validator": "^18.4.3",
+        "@commitlint/execute-rule": "^18.4.3",
+        "@commitlint/resolve-extends": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "@types/node": "^18.11.9",
+        "chalk": "^4.1.0",
+        "cosmiconfig": "^8.3.6",
+        "cosmiconfig-typescript-loader": "^5.0.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "lodash.uniq": "^4.5.0",
+        "resolve-from": "^5.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/message": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/message/-/message-18.4.3.tgz",
+      "integrity": "sha512-ddJ7AztWUIoEMAXoewx45lKEYEOeOlBVWjk8hDMUGpprkuvWULpaXczqdjwVtjrKT3JhhN+gMs8pm5G3vB2how==",
+      "dev": true
+    },
+    "@commitlint/parse": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/parse/-/parse-18.4.3.tgz",
+      "integrity": "sha512-eoH7CXM9L+/Me96KVcfJ27EIIbA5P9sqw3DqjJhRYuhaULIsPHFs5S5GBDCqT0vKZQDx0DgxhMpW6AQbnKrFtA==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^18.4.3",
+        "conventional-changelog-angular": "^7.0.0",
+        "conventional-commits-parser": "^5.0.0"
+      }
+    },
+    "@commitlint/read": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/read/-/read-18.4.3.tgz",
+      "integrity": "sha512-H4HGxaYA6OBCimZAtghL+B+SWu8ep4X7BwgmedmqWZRHxRLcX2q0bWBtUm5FsMbluxbOfrJwOs/Z0ah4roP/GQ==",
+      "dev": true,
+      "requires": {
+        "@commitlint/top-level": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "fs-extra": "^11.0.0",
+        "git-raw-commits": "^2.0.11",
+        "minimist": "^1.2.6"
+      }
+    },
+    "@commitlint/resolve-extends": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/resolve-extends/-/resolve-extends-18.4.3.tgz",
+      "integrity": "sha512-30sk04LZWf8+SDgJrbJCjM90gTg2LxsD9cykCFeFu+JFHvBFq5ugzp2eO/DJGylAdVaqxej3c7eTSE64hR/lnw==",
+      "dev": true,
+      "requires": {
+        "@commitlint/config-validator": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "import-fresh": "^3.0.0",
+        "lodash.mergewith": "^4.6.2",
+        "resolve-from": "^5.0.0",
+        "resolve-global": "^1.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/rules": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/rules/-/rules-18.4.3.tgz",
+      "integrity": "sha512-8KIeukDf45BiY+Lul1T0imSNXF0sMrlLG6JpLLKolkmYVQ6PxxoNOriwyZ3UTFFpaVbPy0rcITaV7U9JCAfDTA==",
+      "dev": true,
+      "requires": {
+        "@commitlint/ensure": "^18.4.3",
+        "@commitlint/message": "^18.4.3",
+        "@commitlint/to-lines": "^18.4.3",
+        "@commitlint/types": "^18.4.3",
+        "execa": "^5.0.0"
+      }
+    },
+    "@commitlint/to-lines": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/to-lines/-/to-lines-18.4.3.tgz",
+      "integrity": "sha512-fy1TAleik4Zfru1RJ8ZU6cOSvgSVhUellxd3WZV1D5RwHZETt1sZdcA4mQN2y3VcIZsUNKkW0Mq8CM9/L9harQ==",
+      "dev": true
+    },
+    "@commitlint/top-level": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/top-level/-/top-level-18.4.3.tgz",
+      "integrity": "sha512-E6fJPBLPFL5R8+XUNSYkj4HekIOuGMyJo3mIx2PkYc3clel+pcWQ7TConqXxNWW4x1ugigiIY2RGot55qUq1hw==",
+      "dev": true,
+      "requires": {
+        "find-up": "^5.0.0"
+      }
+    },
+    "@commitlint/types": {
+      "version": "18.4.3",
+      "resolved": "https://registry.npmmirror.com/@commitlint/types/-/types-18.4.3.tgz",
+      "integrity": "sha512-cvzx+vtY/I2hVBZHCLrpoh+sA0hfuzHwDc+BAFPimYLjJkpHnghQM+z8W/KyLGkygJh3BtI3xXXq+dKjnSWEmA==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.1.0"
+      }
+    },
+    "@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "dev": true,
+      "optional": true
+    },
+    "@eslint-community/eslint-utils": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^3.3.0"
+      }
+    },
+    "@eslint-community/regexpp": {
+      "version": "4.10.0",
+      "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+      "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+      "dev": true
+    },
+    "@eslint/eslintrc": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+      "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.6.0",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "@eslint/js": {
+      "version": "8.56.0",
+      "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.56.0.tgz",
+      "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+      "dev": true
+    },
+    "@humanwhocodes/config-array": {
+      "version": "0.11.13",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
+      "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
+      "dev": true,
+      "requires": {
+        "@humanwhocodes/object-schema": "^2.0.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true
+    },
+    "@humanwhocodes/object-schema": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
+      "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
+      "dev": true
+    },
+    "@icon-park/vue-next": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmmirror.com/@icon-park/vue-next/-/vue-next-1.4.2.tgz",
+      "integrity": "sha512-+QklF255wkfBOabY+xw6FAI0Bwln/RhdwCunNy/9sKdKuChtaU67QZqU67KGAvZUTeeBgsL+yaHHxqfQeGZXEQ==",
+      "requires": {}
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+    },
+    "@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      }
+    },
+    "@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true
+    },
+    "@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      }
+    },
+    "@popperjs/core": {
+      "version": "2.11.8",
+      "resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.8.tgz",
+      "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+    },
+    "@rollup/rollup-android-arm-eabi": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz",
+      "integrity": "sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-android-arm64": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.0.tgz",
+      "integrity": "sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-darwin-arm64": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.0.tgz",
+      "integrity": "sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-darwin-x64": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.0.tgz",
+      "integrity": "sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.0.tgz",
+      "integrity": "sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.0.tgz",
+      "integrity": "sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.0.tgz",
+      "integrity": "sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm64-musl": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.0.tgz",
+      "integrity": "sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.0.tgz",
+      "integrity": "sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.0.tgz",
+      "integrity": "sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.0.tgz",
+      "integrity": "sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-x64-gnu": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.0.tgz",
+      "integrity": "sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-x64-musl": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.0.tgz",
+      "integrity": "sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.0.tgz",
+      "integrity": "sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.0.tgz",
+      "integrity": "sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-x64-msvc": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.0.tgz",
+      "integrity": "sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==",
+      "dev": true,
+      "optional": true
+    },
+    "@rushstack/eslint-patch": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.6.1.tgz",
+      "integrity": "sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==",
+      "dev": true
+    },
+    "@tsconfig/node18": {
+      "version": "18.2.2",
+      "resolved": "https://registry.npmmirror.com/@tsconfig/node18/-/node18-18.2.2.tgz",
+      "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==",
+      "dev": true
+    },
+    "@types/crypto-js": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.1.tgz",
+      "integrity": "sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==",
+      "dev": true
+    },
+    "@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
+    "@types/file-saver": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmmirror.com/@types/file-saver/-/file-saver-2.0.7.tgz",
+      "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
+      "dev": true
+    },
+    "@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true
+    },
+    "@types/lodash": {
+      "version": "4.14.202",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
+      "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
+      "dev": true
+    },
+    "@types/minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "18.19.4",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.4.tgz",
+      "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==",
+      "requires": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "@types/normalize-package-data": {
+      "version": "2.4.4",
+      "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
+      "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
+      "dev": true
+    },
+    "@types/semver": {
+      "version": "7.5.6",
+      "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.5.6.tgz",
+      "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
+      "dev": true
+    },
+    "@types/svg-arc-to-cubic-bezier": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/@types/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.2.tgz",
+      "integrity": "sha512-XQtKy9lmkKlV+c3Jelo7kxNPw7qOqIq3GcnOhywGZHF7zw5D5m+Ssigbmf3Turbe/A8Ur+lRh8TYjuxXKvyivw==",
+      "dev": true
+    },
+    "@types/tinycolor2": {
+      "version": "1.4.6",
+      "resolved": "https://registry.npmmirror.com/@types/tinycolor2/-/tinycolor2-1.4.6.tgz",
+      "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==",
+      "dev": true
+    },
+    "@typescript-eslint/eslint-plugin": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.16.0.tgz",
+      "integrity": "sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/regexpp": "^4.5.1",
+        "@typescript-eslint/scope-manager": "6.16.0",
+        "@typescript-eslint/type-utils": "6.16.0",
+        "@typescript-eslint/utils": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0",
+        "debug": "^4.3.4",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.4",
+        "natural-compare": "^1.4.0",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      }
+    },
+    "@typescript-eslint/parser": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-6.16.0.tgz",
+      "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/scope-manager": "6.16.0",
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/typescript-estree": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0",
+        "debug": "^4.3.4"
+      }
+    },
+    "@typescript-eslint/scope-manager": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz",
+      "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0"
+      }
+    },
+    "@typescript-eslint/type-utils": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-6.16.0.tgz",
+      "integrity": "sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/typescript-estree": "6.16.0",
+        "@typescript-eslint/utils": "6.16.0",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^1.0.1"
+      }
+    },
+    "@typescript-eslint/types": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-6.16.0.tgz",
+      "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==",
+      "dev": true
+    },
+    "@typescript-eslint/typescript-estree": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz",
+      "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/visitor-keys": "6.16.0",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "minimatch": "9.0.3",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      }
+    },
+    "@typescript-eslint/utils": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-6.16.0.tgz",
+      "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "@types/json-schema": "^7.0.12",
+        "@types/semver": "^7.5.0",
+        "@typescript-eslint/scope-manager": "6.16.0",
+        "@typescript-eslint/types": "6.16.0",
+        "@typescript-eslint/typescript-estree": "6.16.0",
+        "semver": "^7.5.4"
+      }
+    },
+    "@typescript-eslint/visitor-keys": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz",
+      "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "6.16.0",
+        "eslint-visitor-keys": "^3.4.1"
+      }
+    },
+    "@ungap/structured-clone": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+      "dev": true
+    },
+    "@vitejs/plugin-vue": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.1.0.tgz",
+      "integrity": "sha512-QMRxARyrdiwi1mj3AW4fLByoHTavreXq0itdEW696EihXglf1MB3D4C2gBvE0jMPH29ZjC3iK8aIaUMLf4EOGA==",
+      "dev": true,
+      "requires": {}
+    },
+    "@volar/language-core": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==",
+      "dev": true,
+      "requires": {
+        "@volar/source-map": "2.4.0-alpha.18"
+      }
+    },
+    "@volar/source-map": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g==",
+      "dev": true
+    },
+    "@volar/typescript": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==",
+      "dev": true,
+      "requires": {
+        "@volar/language-core": "2.4.0-alpha.18",
+        "path-browserify": "^1.0.1",
+        "vscode-uri": "^3.0.8"
+      }
+    },
+    "@vue/compiler-core": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.34.tgz",
+      "integrity": "sha512-Z0izUf32+wAnQewjHu+pQf1yw00EGOmevl1kE+ljjjMe7oEfpQ+BI3/JNK7yMB4IrUsqLDmPecUrpj3mCP+yJQ==",
+      "requires": {
+        "@babel/parser": "^7.24.7",
+        "@vue/shared": "3.4.34",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.34.tgz",
+      "integrity": "sha512-3PUOTS1h5cskdOJMExCu2TInXuM0j60DRPpSCJDqOCupCfUZCJoyQmKtRmA8EgDNZ5kcEE7vketamRZfrEuVDw==",
+      "requires": {
+        "@vue/compiler-core": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.34.tgz",
+      "integrity": "sha512-x6lm0UrM03jjDXTPZgD9Ad8bIVD1ifWNit2EaWQIZB5CULr46+FbLQ5RpK7AXtDHGjx9rmvC7QRCTjsiGkAwRw==",
+      "requires": {
+        "@babel/parser": "^7.24.7",
+        "@vue/compiler-core": "3.4.34",
+        "@vue/compiler-dom": "3.4.34",
+        "@vue/compiler-ssr": "3.4.34",
+        "@vue/shared": "3.4.34",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.10",
+        "postcss": "^8.4.39",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.34.tgz",
+      "integrity": "sha512-8TDBcLaTrFm5rnF+Qm4BlliaopJgqJ28Nsrc80qazynm5aJO+Emu7y0RWw34L8dNnTRdcVBpWzJxhGYzsoVu4g==",
+      "requires": {
+        "@vue/compiler-dom": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "@vue/compiler-vue2": {
+      "version": "2.7.16",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
+      "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
+      "dev": true,
+      "requires": {
+        "de-indent": "^1.0.2",
+        "he": "^1.2.0"
+      }
+    },
+    "@vue/devtools-api": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
+      "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
+    },
+    "@vue/eslint-config-typescript": {
+      "version": "12.0.0",
+      "resolved": "https://registry.npmmirror.com/@vue/eslint-config-typescript/-/eslint-config-typescript-12.0.0.tgz",
+      "integrity": "sha512-StxLFet2Qe97T8+7L8pGlhYBBr8Eg05LPuTDVopQV6il+SK6qqom59BA/rcFipUef2jD8P2X44Vd8tMFytfvlg==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/eslint-plugin": "^6.7.0",
+        "@typescript-eslint/parser": "^6.7.0",
+        "vue-eslint-parser": "^9.3.1"
+      }
+    },
+    "@vue/language-core": {
+      "version": "2.0.29",
+      "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.0.29.tgz",
+      "integrity": "sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==",
+      "dev": true,
+      "requires": {
+        "@volar/language-core": "~2.4.0-alpha.18",
+        "@vue/compiler-dom": "^3.4.0",
+        "@vue/compiler-vue2": "^2.7.16",
+        "@vue/shared": "^3.4.0",
+        "computeds": "^0.0.1",
+        "minimatch": "^9.0.3",
+        "muggle-string": "^0.4.1",
+        "path-browserify": "^1.0.1"
+      }
+    },
+    "@vue/reactivity": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.34.tgz",
+      "integrity": "sha512-ua+Lo+wBRlBEX9TtgPOShE2JwIO7p6BTZ7t1KZVPoaBRfqbC7N3c8Mpzicx173fXxx5VXeU6ykiHo7WgLzJQDA==",
+      "requires": {
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.34.tgz",
+      "integrity": "sha512-PXhkiRPwcPGJ1BnyBZFI96GfInCVskd0HPNIAZn7i3YOmLbtbTZpB7/kDTwC1W7IqdGPkTVC63IS7J2nZs4Ebg==",
+      "requires": {
+        "@vue/reactivity": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.34.tgz",
+      "integrity": "sha512-dXqIe+RqFAK2Euak4UsvbIupalrhc67OuQKpD7HJ3W2fv8jlqvI7szfBCsAEcE8o/wyNpkloxB6J8viuF/E3gw==",
+      "requires": {
+        "@vue/reactivity": "3.4.34",
+        "@vue/runtime-core": "3.4.34",
+        "@vue/shared": "3.4.34",
+        "csstype": "^3.1.3"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.34.tgz",
+      "integrity": "sha512-GeyEUfMVRZMD/mZcNONEqg7MiU10QQ1DB3O/Qr6+8uXpbwdlmVgQ5Qs1/ZUAFX1X2UUtqMoGrDRbxdWfOJFT7Q==",
+      "requires": {
+        "@vue/compiler-ssr": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.34.tgz",
+      "integrity": "sha512-x5LmiRLpRsd9KTjAB8MPKf0CDPMcuItjP0gbNqFCIgL1I8iYp4zglhj9w9FPCdIbHG2M91RVeIbArFfFTz9I3A=="
+    },
+    "@vue/tsconfig": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.5.1.tgz",
+      "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
+      "dev": true
+    },
+    "acorn": {
+      "version": "8.11.3",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
+      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "requires": {}
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "animate.css": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz",
+      "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
+    },
+    "ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^2.0.1"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
+    "array-ify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/array-ify/-/array-ify-1.0.0.tgz",
+      "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==",
+      "dev": true
+    },
+    "array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true
+    },
+    "arrify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true
+    },
+    "boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true
+    },
+    "camelcase-keys": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
+      "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.3.1",
+        "map-obj": "^4.0.0",
+        "quick-lru": "^4.0.1"
+      }
+    },
+    "chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      }
+    },
+    "chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.1"
+          }
+        }
+      }
+    },
+    "clipboard": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz",
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+      "requires": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
+    "cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "requires": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "requires": {
+        "color-name": "~1.1.4"
+      }
+    },
+    "color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "compare-func": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/compare-func/-/compare-func-2.0.0.tgz",
+      "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+      "dev": true,
+      "requires": {
+        "array-ify": "^1.0.0",
+        "dot-prop": "^5.1.0"
+      }
+    },
+    "computeds": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/computeds/-/computeds-0.0.1.tgz",
+      "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
+    },
+    "conventional-changelog-angular": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+      "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+      "dev": true,
+      "requires": {
+        "compare-func": "^2.0.0"
+      }
+    },
+    "conventional-changelog-conventionalcommits": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmmirror.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz",
+      "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==",
+      "dev": true,
+      "requires": {
+        "compare-func": "^2.0.0"
+      }
+    },
+    "conventional-commits-parser": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+      "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+      "dev": true,
+      "requires": {
+        "is-text-path": "^2.0.0",
+        "JSONStream": "^1.3.5",
+        "meow": "^12.0.1",
+        "split2": "^4.0.0"
+      }
+    },
+    "core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
+    "cosmiconfig": {
+      "version": "8.3.6",
+      "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+      "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+      "dev": true,
+      "requires": {
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0",
+        "path-type": "^4.0.0"
+      },
+      "dependencies": {
+        "json-parse-even-better-errors": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+          "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+          "dev": true
+        },
+        "lines-and-columns": {
+          "version": "1.2.4",
+          "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+          "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+          "dev": true
+        },
+        "parse-json": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
+          "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.0.0",
+            "error-ex": "^1.3.1",
+            "json-parse-even-better-errors": "^2.3.0",
+            "lines-and-columns": "^1.1.6"
+          }
+        }
+      }
+    },
+    "cosmiconfig-typescript-loader": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz",
+      "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==",
+      "dev": true,
+      "requires": {
+        "jiti": "^1.19.1"
+      }
+    },
+    "cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
+      "requires": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      }
+    },
+    "crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
+    },
+    "cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true
+    },
+    "csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
+    "dargs": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/dargs/-/dargs-7.0.0.tgz",
+      "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==",
+      "dev": true
+    },
+    "de-indent": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
+      "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+      "dev": true
+    },
+    "debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+      "dev": true,
+      "requires": {
+        "ms": "2.1.2"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+      "dev": true
+    },
+    "decamelize-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz",
+      "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==",
+      "dev": true,
+      "requires": {
+        "decamelize": "^1.1.0",
+        "map-obj": "^1.0.0"
+      },
+      "dependencies": {
+        "map-obj": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-1.0.1.tgz",
+          "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==",
+          "dev": true
+        }
+      }
+    },
+    "deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
+    },
+    "delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
+    "dexie": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/dexie/-/dexie-3.0.3.tgz",
+      "integrity": "sha512-BSFhGpngnCl1DOr+8YNwBDobRMH0ziJs2vts69VilwetHYOtEDcLqo7d/XiIphM0tJZ2rPPyAGd31lgH2Ln3nw=="
+    },
+    "dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "requires": {
+        "path-type": "^4.0.0"
+      }
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "dot-prop": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-5.3.0.tgz",
+      "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+      "dev": true,
+      "requires": {
+        "is-obj": "^2.0.0"
+      }
+    },
+    "echarts": {
+      "version": "5.5.1",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz",
+      "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==",
+      "requires": {
+        "tslib": "2.3.0",
+        "zrender": "5.6.0"
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "requires": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true
+    },
+    "escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true
+    },
+    "eslint": {
+      "version": "8.56.0",
+      "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.56.0.tgz",
+      "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.6.1",
+        "@eslint/eslintrc": "^2.1.4",
+        "@eslint/js": "8.56.0",
+        "@humanwhocodes/config-array": "^0.11.13",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "@ungap/structured-clone": "^1.2.0",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.2.2",
+        "eslint-visitor-keys": "^3.4.3",
+        "espree": "^9.6.1",
+        "esquery": "^1.4.2",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3",
+        "strip-ansi": "^6.0.1",
+        "text-table": "^0.2.0"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "eslint-plugin-vue": {
+      "version": "9.19.2",
+      "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.19.2.tgz",
+      "integrity": "sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "natural-compare": "^1.4.0",
+        "nth-check": "^2.1.1",
+        "postcss-selector-parser": "^6.0.13",
+        "semver": "^7.5.4",
+        "vue-eslint-parser": "^9.3.1",
+        "xml-name-validator": "^4.0.0"
+      }
+    },
+    "eslint-scope": {
+      "version": "7.2.2",
+      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz",
+      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true
+    },
+    "espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "requires": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      }
+    },
+    "esquery": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.5.0.tgz",
+      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.1.0"
+      }
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      }
+    },
+    "estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true
+    },
+    "estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true
+    },
+    "execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      }
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-glob": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz",
+      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.1"
+          }
+        }
+      }
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true
+    },
+    "fastq": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.16.0.tgz",
+      "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^3.0.4"
+      }
+    },
+    "file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      }
+    },
+    "flat-cache": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz",
+      "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+      "dev": true,
+      "requires": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.3",
+        "rimraf": "^3.0.2"
+      }
+    },
+    "flatted": {
+      "version": "3.2.9",
+      "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.2.9.tgz",
+      "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+      "dev": true
+    },
+    "fs-extra": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz",
+      "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true
+    },
+    "get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true
+    },
+    "git-raw-commits": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmmirror.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz",
+      "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==",
+      "dev": true,
+      "requires": {
+        "dargs": "^7.0.0",
+        "lodash": "^4.17.15",
+        "meow": "^8.0.0",
+        "split2": "^3.0.0",
+        "through2": "^4.0.0"
+      },
+      "dependencies": {
+        "hosted-git-info": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+          "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "meow": {
+          "version": "8.1.2",
+          "resolved": "https://registry.npmmirror.com/meow/-/meow-8.1.2.tgz",
+          "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==",
+          "dev": true,
+          "requires": {
+            "@types/minimist": "^1.2.0",
+            "camelcase-keys": "^6.2.2",
+            "decamelize-keys": "^1.1.0",
+            "hard-rejection": "^2.1.0",
+            "minimist-options": "4.1.0",
+            "normalize-package-data": "^3.0.0",
+            "read-pkg-up": "^7.0.1",
+            "redent": "^3.0.0",
+            "trim-newlines": "^3.0.0",
+            "type-fest": "^0.18.0",
+            "yargs-parser": "^20.2.3"
+          }
+        },
+        "normalize-package-data": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
+          "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==",
+          "dev": true,
+          "requires": {
+            "hosted-git-info": "^4.0.1",
+            "is-core-module": "^2.5.0",
+            "semver": "^7.3.4",
+            "validate-npm-package-license": "^3.0.1"
+          }
+        },
+        "readable-stream": {
+          "version": "3.6.2",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+          "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "split2": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmmirror.com/split2/-/split2-3.2.2.tgz",
+          "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+          "dev": true,
+          "requires": {
+            "readable-stream": "^3.0.0"
+          }
+        },
+        "through2": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmmirror.com/through2/-/through2-4.0.2.tgz",
+          "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
+          "dev": true,
+          "requires": {
+            "readable-stream": "3"
+          }
+        },
+        "type-fest": {
+          "version": "0.18.1",
+          "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.18.1.tgz",
+          "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
+          "dev": true
+        },
+        "yargs-parser": {
+          "version": "20.2.9",
+          "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz",
+          "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+          "dev": true
+        }
+      }
+    },
+    "glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.3"
+      }
+    },
+    "global-dirs": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmmirror.com/global-dirs/-/global-dirs-0.1.1.tgz",
+      "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==",
+      "dev": true,
+      "requires": {
+        "ini": "^1.3.4"
+      }
+    },
+    "globals": {
+      "version": "13.24.0",
+      "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+      "dev": true,
+      "requires": {
+        "type-fest": "^0.20.2"
+      }
+    },
+    "globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "requires": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      }
+    },
+    "good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+      "requires": {
+        "delegate": "^3.1.2"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "graphemer": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz",
+      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+      "dev": true
+    },
+    "hard-rejection": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/hard-rejection/-/hard-rejection-2.1.0.tgz",
+      "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true
+    },
+    "hasown": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz",
+      "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.2"
+      }
+    },
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true
+    },
+    "hfmath": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/hfmath/-/hfmath-0.0.2.tgz",
+      "integrity": "sha512-cKUi0yiQLGfLgs8+3Iw5nAiqSH13Knp7vCf0G1vlF5nfiKKO1XmxNagMvyp0F4ZvUNaHpRGTkmc7nowCy1S58g=="
+    },
+    "hosted-git-info": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-7.0.1.tgz",
+      "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^10.0.1"
+      }
+    },
+    "html-to-image": {
+      "version": "1.11.11",
+      "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz",
+      "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
+    },
+    "https": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/https/-/https-1.0.0.tgz",
+      "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="
+    },
+    "human-signals": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true
+    },
+    "husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmmirror.com/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true
+    },
+    "ignore": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.0.tgz",
+      "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+      "dev": true
+    },
+    "image-size": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/image-size/-/image-size-1.1.0.tgz",
+      "integrity": "sha512-asnTHw2K8OlqT5kVnQwX+AGKQqpvLo95LbNzQ/C0ln3yzentZmAdd0ygoD004VC4Kkd4PV7J2iaPQkqwp9yuTw==",
+      "requires": {
+        "queue": "6.0.2"
+      }
+    },
+    "immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
+    "immutable": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.4.tgz",
+      "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true
+    },
+    "indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "dev": true
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-core-module": {
+      "version": "2.13.1",
+      "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz",
+      "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+      "dev": true,
+      "requires": {
+        "hasown": "^2.0.0"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-obj": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz",
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+      "dev": true
+    },
+    "is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true
+    },
+    "is-plain-obj": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+      "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+      "dev": true
+    },
+    "is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true
+    },
+    "is-text-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/is-text-path/-/is-text-path-2.0.0.tgz",
+      "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+      "dev": true,
+      "requires": {
+        "text-extensions": "^2.0.0"
+      }
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true
+    },
+    "jiti": {
+      "version": "1.21.0",
+      "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.0.tgz",
+      "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+      "dev": true
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "requires": {
+        "argparse": "^2.0.1"
+      }
+    },
+    "json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true
+    },
+    "json-parse-even-better-errors": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz",
+      "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true
+    },
+    "jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.6",
+        "universalify": "^2.0.0"
+      }
+    },
+    "jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+      "dev": true
+    },
+    "JSONStream": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmmirror.com/JSONStream/-/JSONStream-1.3.5.tgz",
+      "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+      "dev": true,
+      "requires": {
+        "jsonparse": "^1.2.0",
+        "through": ">=2.2.7 <3"
+      }
+    },
+    "jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "requires": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "requires": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true
+    },
+    "levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      }
+    },
+    "lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "requires": {
+        "immediate": "~3.0.5"
+      }
+    },
+    "lines-and-columns": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz",
+      "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==",
+      "dev": true
+    },
+    "locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^5.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "dev": true
+    },
+    "lodash.isfunction": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmmirror.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
+      "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==",
+      "dev": true
+    },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "dev": true
+    },
+    "lodash.kebabcase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+      "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+      "dev": true
+    },
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "lodash.mergewith": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+      "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+      "dev": true
+    },
+    "lodash.snakecase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+      "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+      "dev": true
+    },
+    "lodash.startcase": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmmirror.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+      "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+      "dev": true
+    },
+    "lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+      "dev": true
+    },
+    "lodash.upperfirst": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+      "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+      "dev": true
+    },
+    "lru-cache": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.1.0.tgz",
+      "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==",
+      "dev": true
+    },
+    "magic-string": {
+      "version": "0.30.10",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz",
+      "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
+      "requires": {
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      }
+    },
+    "map-obj": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-4.3.0.tgz",
+      "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
+      "dev": true
+    },
+    "memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmmirror.com/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
+      "dev": true
+    },
+    "meow": {
+      "version": "12.1.1",
+      "resolved": "https://registry.npmmirror.com/meow/-/meow-12.1.1.tgz",
+      "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+      "dev": true
+    },
+    "merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
+    "merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dev": true,
+      "requires": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      }
+    },
+    "mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true
+    },
+    "min-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz",
+      "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz",
+      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^2.0.1"
+      }
+    },
+    "minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true
+    },
+    "minimist-options": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz",
+      "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==",
+      "dev": true,
+      "requires": {
+        "arrify": "^1.0.1",
+        "is-plain-obj": "^1.1.0",
+        "kind-of": "^6.0.3"
+      }
+    },
+    "mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "muggle-string": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz",
+      "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+      "dev": true
+    },
+    "nanoid": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.0.7.tgz",
+      "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ=="
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true
+    },
+    "normalize-package-data": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-6.0.0.tgz",
+      "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^7.0.0",
+        "is-core-module": "^2.8.1",
+        "semver": "^7.3.5",
+        "validate-npm-package-license": "^3.0.4"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "npm-run-all2": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmmirror.com/npm-run-all2/-/npm-run-all2-6.1.1.tgz",
+      "integrity": "sha512-lWLbkPZ5BSdXtN8lR+0rc8caKoPdymycpZksyDEC9MOBvfdwTXZ0uVhb7bMcGeXv2/BKtfQuo6Zn3zfc8rxNXA==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^6.2.1",
+        "cross-spawn": "^7.0.3",
+        "memorystream": "^0.3.1",
+        "minimatch": "^9.0.0",
+        "pidtree": "^0.6.0",
+        "read-pkg": "^8.0.0",
+        "shell-quote": "^1.7.3"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "6.2.1",
+          "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz",
+          "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+          "dev": true
+        }
+      }
+    },
+    "npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
+      "requires": {
+        "path-key": "^3.0.0"
+      }
+    },
+    "nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0"
+      }
+    },
+    "number-precision": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz",
+      "integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ=="
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^2.1.0"
+      }
+    },
+    "optionator": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz",
+      "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+      "dev": true,
+      "requires": {
+        "@aashutoshrathi/word-wrap": "^1.2.3",
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0"
+      }
+    },
+    "orderedmap": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/orderedmap/-/orderedmap-2.1.1.tgz",
+      "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="
+    },
+    "p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "requires": {
+        "yocto-queue": "^0.1.0"
+      }
+    },
+    "p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "requires": {
+        "p-limit": "^3.0.2"
+      }
+    },
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true
+    },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "parse-json": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-7.1.1.tgz",
+      "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.21.4",
+        "error-ex": "^1.3.2",
+        "json-parse-even-better-errors": "^3.0.0",
+        "lines-and-columns": "^2.0.3",
+        "type-fest": "^3.8.0"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "3.13.1",
+          "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz",
+          "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
+          "dev": true
+        }
+      }
+    },
+    "path-browserify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+      "dev": true
+    },
+    "path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true
+    },
+    "path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true
+    },
+    "picocolors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz",
+      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+    },
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true
+    },
+    "pidtree": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz",
+      "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+      "dev": true
+    },
+    "pinia": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz",
+      "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
+      "requires": {
+        "@vue/devtools-api": "^6.5.0",
+        "vue-demi": ">=0.14.5"
+      },
+      "dependencies": {
+        "vue-demi": {
+          "version": "0.14.6",
+          "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz",
+          "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
+          "requires": {}
+        }
+      }
+    },
+    "postcss": {
+      "version": "8.4.39",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.39.tgz",
+      "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
+      "requires": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.1",
+        "source-map-js": "^1.2.0"
+      },
+      "dependencies": {
+        "nanoid": {
+          "version": "3.3.7",
+          "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+          "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
+        }
+      }
+    },
+    "postcss-selector-parser": {
+      "version": "6.0.15",
+      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
+      "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
+      "dev": true,
+      "requires": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "pptxgenjs": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmmirror.com/pptxgenjs/-/pptxgenjs-3.12.0.tgz",
+      "integrity": "sha512-ZozkYKWb1MoPR4ucw3/aFYlHkVIJxo9czikEclcUVnS4Iw/M+r+TEwdlB3fyAWO9JY1USxJDt0Y0/r15IR/RUA==",
+      "requires": {
+        "@types/node": "^18.7.3",
+        "https": "^1.0.0",
+        "image-size": "^1.0.0",
+        "jszip": "^3.7.1"
+      }
+    },
+    "pptxtojson": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/pptxtojson/-/pptxtojson-1.0.3.tgz",
+      "integrity": "sha512-Q7tvtqFUm9x4CGRv/BUt69yeJS1RtIKBuh/G/gpxdtSbm7zAVeTPbPRMS+1hfliVXhzYJDgbg4zc8F4hwmq71A==",
+      "requires": {
+        "jszip": "^3.10.1",
+        "tinycolor2": "1.6.0",
+        "txml": "^5.1.1"
+      }
+    },
+    "prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
+    "prosemirror-commands": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/prosemirror-commands/-/prosemirror-commands-1.6.0.tgz",
+      "integrity": "sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==",
+      "requires": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "prosemirror-dropcursor": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz",
+      "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==",
+      "requires": {
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.1.0",
+        "prosemirror-view": "^1.1.0"
+      }
+    },
+    "prosemirror-gapcursor": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz",
+      "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==",
+      "requires": {
+        "prosemirror-keymap": "^1.0.0",
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-view": "^1.0.0"
+      }
+    },
+    "prosemirror-history": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-history/-/prosemirror-history-1.3.2.tgz",
+      "integrity": "sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==",
+      "requires": {
+        "prosemirror-state": "^1.2.2",
+        "prosemirror-transform": "^1.0.0",
+        "prosemirror-view": "^1.31.0",
+        "rope-sequence": "^1.3.0"
+      }
+    },
+    "prosemirror-inputrules": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz",
+      "integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==",
+      "requires": {
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "prosemirror-keymap": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz",
+      "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==",
+      "requires": {
+        "prosemirror-state": "^1.0.0",
+        "w3c-keyname": "^2.2.0"
+      }
+    },
+    "prosemirror-model": {
+      "version": "1.22.2",
+      "resolved": "https://registry.npmmirror.com/prosemirror-model/-/prosemirror-model-1.22.2.tgz",
+      "integrity": "sha512-I4lS7HHIW47D0Xv/gWmi4iUWcQIDYaJKd8Hk4+lcSps+553FlQrhmxtItpEvTr75iAruhzVShVp6WUwsT6Boww==",
+      "requires": {
+        "orderedmap": "^2.0.0"
+      }
+    },
+    "prosemirror-schema-basic": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz",
+      "integrity": "sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==",
+      "requires": {
+        "prosemirror-model": "^1.19.0"
+      }
+    },
+    "prosemirror-schema-list": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/prosemirror-schema-list/-/prosemirror-schema-list-1.4.1.tgz",
+      "integrity": "sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==",
+      "requires": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.7.3"
+      }
+    },
+    "prosemirror-state": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmmirror.com/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
+      "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
+      "requires": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-transform": "^1.0.0",
+        "prosemirror-view": "^1.27.0"
+      }
+    },
+    "prosemirror-transform": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/prosemirror-transform/-/prosemirror-transform-1.8.0.tgz",
+      "integrity": "sha512-BaSBsIMv52F1BVVMvOmp1yzD3u65uC3HTzCBQV1WDPqJRQ2LuHKcyfn0jwqodo8sR9vVzMzZyI+Dal5W9E6a9A==",
+      "requires": {
+        "prosemirror-model": "^1.0.0"
+      }
+    },
+    "prosemirror-view": {
+      "version": "1.33.9",
+      "resolved": "https://registry.npmmirror.com/prosemirror-view/-/prosemirror-view-1.33.9.tgz",
+      "integrity": "sha512-xV1A0Vz9cIcEnwmMhKKFAOkfIp8XmJRnaZoPqNXrPS7EK5n11Ov8V76KhR0RsfQd/SIzmWY+bg+M44A2Lx/Nnw==",
+      "requires": {
+        "prosemirror-model": "^1.20.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.1.0"
+      }
+    },
+    "punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true
+    },
+    "queue": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/queue/-/queue-6.0.2.tgz",
+      "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
+      "requires": {
+        "inherits": "~2.0.3"
+      }
+    },
+    "queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true
+    },
+    "quick-lru": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-4.0.1.tgz",
+      "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
+      "dev": true
+    },
+    "read-pkg": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-8.1.0.tgz",
+      "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==",
+      "dev": true,
+      "requires": {
+        "@types/normalize-package-data": "^2.4.1",
+        "normalize-package-data": "^6.0.0",
+        "parse-json": "^7.0.0",
+        "type-fest": "^4.2.0"
+      },
+      "dependencies": {
+        "type-fest": {
+          "version": "4.9.0",
+          "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-4.9.0.tgz",
+          "integrity": "sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==",
+          "dev": true
+        }
+      }
+    },
+    "read-pkg-up": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+      "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+      "dev": true,
+      "requires": {
+        "find-up": "^4.1.0",
+        "read-pkg": "^5.2.0",
+        "type-fest": "^0.8.1"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "hosted-git-info": {
+          "version": "2.8.9",
+          "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+          "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+          "dev": true
+        },
+        "json-parse-even-better-errors": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+          "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+          "dev": true
+        },
+        "lines-and-columns": {
+          "version": "1.2.4",
+          "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+          "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+          "dev": true
+        },
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^4.1.0"
+          }
+        },
+        "normalize-package-data": {
+          "version": "2.5.0",
+          "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+          "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+          "dev": true,
+          "requires": {
+            "hosted-git-info": "^2.1.4",
+            "resolve": "^1.10.0",
+            "semver": "2 || 3 || 4 || 5",
+            "validate-npm-package-license": "^3.0.1"
+          }
+        },
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.2.0"
+          }
+        },
+        "parse-json": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
+          "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.0.0",
+            "error-ex": "^1.3.1",
+            "json-parse-even-better-errors": "^2.3.0",
+            "lines-and-columns": "^1.1.6"
+          }
+        },
+        "read-pkg": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz",
+          "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+          "dev": true,
+          "requires": {
+            "@types/normalize-package-data": "^2.4.0",
+            "normalize-package-data": "^2.5.0",
+            "parse-json": "^5.0.0",
+            "type-fest": "^0.6.0"
+          },
+          "dependencies": {
+            "type-fest": {
+              "version": "0.6.0",
+              "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz",
+              "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+              "dev": true
+            }
+          }
+        },
+        "semver": {
+          "version": "5.7.2",
+          "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
+          "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+          "dev": true
+        },
+        "type-fest": {
+          "version": "0.8.1",
+          "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz",
+          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+          "dev": true
+        }
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "redent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz",
+      "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+      "dev": true,
+      "requires": {
+        "indent-string": "^4.0.0",
+        "strip-indent": "^3.0.0"
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true
+    },
+    "require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.22.8",
+      "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
+      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+      "dev": true,
+      "requires": {
+        "is-core-module": "^2.13.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      }
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "resolve-global": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-global/-/resolve-global-1.0.0.tgz",
+      "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==",
+      "dev": true,
+      "requires": {
+        "global-dirs": "^0.1.1"
+      }
+    },
+    "reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "rollup": {
+      "version": "4.19.0",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.19.0.tgz",
+      "integrity": "sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==",
+      "dev": true,
+      "requires": {
+        "@rollup/rollup-android-arm-eabi": "4.19.0",
+        "@rollup/rollup-android-arm64": "4.19.0",
+        "@rollup/rollup-darwin-arm64": "4.19.0",
+        "@rollup/rollup-darwin-x64": "4.19.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.19.0",
+        "@rollup/rollup-linux-arm-musleabihf": "4.19.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.19.0",
+        "@rollup/rollup-linux-arm64-musl": "4.19.0",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.19.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.19.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.19.0",
+        "@rollup/rollup-linux-x64-gnu": "4.19.0",
+        "@rollup/rollup-linux-x64-musl": "4.19.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.19.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.19.0",
+        "@rollup/rollup-win32-x64-msvc": "4.19.0",
+        "@types/estree": "1.0.5",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "rope-sequence": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmmirror.com/rope-sequence/-/rope-sequence-1.3.4.tgz",
+      "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="
+    },
+    "run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "requires": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "sass": {
+      "version": "1.69.6",
+      "resolved": "https://registry.npmmirror.com/sass/-/sass-1.69.6.tgz",
+      "integrity": "sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==",
+      "dev": true,
+      "requires": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      }
+    },
+    "select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+    },
+    "semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^6.0.0"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        }
+      }
+    },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
+    "shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^3.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true
+    },
+    "shell-quote": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.1.tgz",
+      "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true
+    },
+    "slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true
+    },
+    "sortablejs": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
+      "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+    },
+    "source-map-js": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz",
+      "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg=="
+    },
+    "spdx-correct": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz",
+      "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.16",
+      "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
+      "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==",
+      "dev": true
+    },
+    "split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "dev": true
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.1"
+      }
+    },
+    "strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true
+    },
+    "strip-indent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz",
+      "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+      "dev": true,
+      "requires": {
+        "min-indent": "^1.0.0"
+      }
+    },
+    "strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^4.0.0"
+      }
+    },
+    "supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true
+    },
+    "svg-arc-to-cubic-bezier": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
+      "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="
+    },
+    "svg-pathdata": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-7.1.0.tgz",
+      "integrity": "sha512-wrvKHXZSYZyODOj5E1l1bMTIo8sR7YCH0E4SA8IgLgMsZq4RypslpYvNSsrdg4ThD6du2KWPyVeKinkqUelGhg==",
+      "requires": {
+        "yerror": "^8.0.0"
+      }
+    },
+    "text-extensions": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmmirror.com/text-extensions/-/text-extensions-2.4.0.tgz",
+      "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+      "dev": true
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+      "dev": true
+    },
+    "through2": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/through2/-/through2-3.0.2.tgz",
+      "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
+      "requires": {
+        "inherits": "^2.0.4",
+        "readable-stream": "2 || 3"
+      }
+    },
+    "tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
+    "tinycolor2": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz",
+      "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
+    },
+    "tippy.js": {
+      "version": "6.3.7",
+      "resolved": "https://registry.npmmirror.com/tippy.js/-/tippy.js-6.3.7.tgz",
+      "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
+      "requires": {
+        "@popperjs/core": "^2.9.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "trim-newlines": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz",
+      "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
+      "dev": true
+    },
+    "ts-api-utils": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
+      "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
+      "dev": true,
+      "requires": {}
+    },
+    "tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+    },
+    "txml": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/txml/-/txml-5.1.1.tgz",
+      "integrity": "sha512-TwMDLnXQ09enNaxybLVvKZU7rqog8LgnuAs4ZYXM0nV0eu10iLsSFwlX3AEknAXXtH1wT3CYfoiXAjyBexcmuw==",
+      "requires": {
+        "through2": "^3.0.1"
+      }
+    },
+    "type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1"
+      }
+    },
+    "type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true
+    },
+    "typescript": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.3.3.tgz",
+      "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+      "devOptional": true
+    },
+    "undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+    },
+    "universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "vite": {
+      "version": "5.3.5",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-5.3.5.tgz",
+      "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==",
+      "dev": true,
+      "requires": {
+        "esbuild": "^0.21.3",
+        "fsevents": "~2.3.3",
+        "postcss": "^8.4.39",
+        "rollup": "^4.13.0"
+      }
+    },
+    "vscode-uri": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.0.8.tgz",
+      "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==",
+      "dev": true
+    },
+    "vue": {
+      "version": "3.4.34",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.34.tgz",
+      "integrity": "sha512-VZze05HWlA3ItreQ/ka7Sx7PoD0/3St8FEiSlSTVgb6l4hL+RjtP2/8g5WQBzZgyf8WG2f+g1bXzC7zggLhAJA==",
+      "requires": {
+        "@vue/compiler-dom": "3.4.34",
+        "@vue/compiler-sfc": "3.4.34",
+        "@vue/runtime-dom": "3.4.34",
+        "@vue/server-renderer": "3.4.34",
+        "@vue/shared": "3.4.34"
+      }
+    },
+    "vue-eslint-parser": {
+      "version": "9.3.2",
+      "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
+      "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.3.4",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.3.1",
+        "esquery": "^1.4.0",
+        "lodash": "^4.17.21",
+        "semver": "^7.3.6"
+      }
+    },
+    "vue-tsc": {
+      "version": "2.0.29",
+      "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.0.29.tgz",
+      "integrity": "sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==",
+      "dev": true,
+      "requires": {
+        "@volar/typescript": "~2.4.0-alpha.18",
+        "@vue/language-core": "2.0.29",
+        "semver": "^7.5.4"
+      }
+    },
+    "vuedraggable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
+      "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+      "requires": {
+        "sortablejs": "1.14.0"
+      }
+    },
+    "w3c-keyname": {
+      "version": "2.2.8",
+      "resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+      "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
+    },
+    "which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
+    },
+    "xml-name-validator": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "dev": true
+    },
+    "y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true
+    },
+    "yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "requires": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      }
+    },
+    "yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true
+    },
+    "yerror": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/yerror/-/yerror-8.0.0.tgz",
+      "integrity": "sha512-FemWD5/UqNm8ffj8oZIbjWXIF2KE0mZssggYpdaQkWDDgXBQ/35PNIxEuz6/YLn9o0kOxDBNJe8x8k9ljD7k/g=="
+    },
+    "yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true
+    },
+    "zrender": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz",
+      "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==",
+      "requires": {
+        "tslib": "2.3.0"
+      }
+    }
+  }
+}

+ 70 - 0
package.json

@@ -0,0 +1,70 @@
+{
+  "name": "pptist",
+  "version": "1.0.0",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "run-p type-check \"build-only {@}\" --",
+    "preview": "vite preview",
+    "build-only": "vite build",
+    "type-check": "vue-tsc --build --force",
+    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
+    "prepare": "husky install"
+  },
+  "dependencies": {
+    "@icon-park/vue-next": "^1.4.2",
+    "animate.css": "^4.1.1",
+    "clipboard": "^2.0.11",
+    "crypto-js": "^4.2.0",
+    "dexie": "3.0.3",
+    "echarts": "^5.5.1",
+    "file-saver": "^2.0.5",
+    "hfmath": "^0.0.2",
+    "html-to-image": "^1.11.11",
+    "lodash": "^4.17.21",
+    "mitt": "^3.0.1",
+    "nanoid": "^5.0.7",
+    "number-precision": "^1.6.0",
+    "pinia": "^2.1.7",
+    "pptxgenjs": "^3.12.0",
+    "pptxtojson": "^1.0.3",
+    "prosemirror-commands": "^1.6.0",
+    "prosemirror-dropcursor": "^1.8.1",
+    "prosemirror-gapcursor": "^1.3.2",
+    "prosemirror-history": "^1.3.2",
+    "prosemirror-inputrules": "^1.4.0",
+    "prosemirror-keymap": "^1.2.2",
+    "prosemirror-model": "^1.22.2",
+    "prosemirror-schema-basic": "^1.2.3",
+    "prosemirror-schema-list": "^1.4.1",
+    "prosemirror-state": "^1.4.3",
+    "prosemirror-view": "^1.33.9",
+    "svg-arc-to-cubic-bezier": "^3.2.0",
+    "svg-pathdata": "^7.1.0",
+    "tinycolor2": "^1.6.0",
+    "tippy.js": "^6.3.7",
+    "vue": "^3.4.34",
+    "vuedraggable": "^4.1.0"
+  },
+  "devDependencies": {
+    "@rushstack/eslint-patch": "^1.3.3",
+    "@tsconfig/node18": "^18.2.2",
+    "@types/crypto-js": "^4.2.1",
+    "@types/file-saver": "^2.0.7",
+    "@types/lodash": "^4.14.202",
+    "@types/node": "^18.19.3",
+    "@types/svg-arc-to-cubic-bezier": "^3.2.2",
+    "@types/tinycolor2": "^1.4.6",
+    "@vitejs/plugin-vue": "^5.1.0",
+    "@vue/eslint-config-typescript": "^12.0.0",
+    "@vue/tsconfig": "^0.5.0",
+    "eslint": "^8.49.0",
+    "eslint-plugin-vue": "^9.17.0",
+    "npm-run-all2": "^6.1.1",
+    "sass": "^1.69.6",
+    "typescript": "~5.3.0",
+    "vite": "^5.3.5",
+    "vue-tsc": "^2.0.29"
+  }
+}

BIN
public/favicon.ico


BIN
public/icons/android-chrome-192x192.png


BIN
public/icons/android-chrome-512x512.png


BIN
public/icons/android-chrome-maskable-192x192.png


BIN
public/icons/android-chrome-maskable-512x512.png


BIN
public/icons/apple-touch-icon-152x152.png


BIN
public/icons/favicon-16x16.png


BIN
public/icons/favicon-32x32.png


+ 54 - 0
src/App.vue

@@ -0,0 +1,54 @@
+<template>
+  <Screen v-if="screening" />
+  <Editor v-else-if="_isPC" />
+  <Mobile v-else />
+</template>
+
+
+
+<script lang="ts" setup>
+import { onMounted } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useScreenStore, useMainStore, useSnapshotStore } from '@/store'
+import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
+import { deleteDiscardedDB } from '@/utils/database'
+import { isPC } from './utils/common'
+
+import Editor from './views/Editor/index.vue'
+import Screen from './views/Screen/index.vue'
+import Mobile from './views/Mobile/index.vue'
+
+const _isPC = isPC()
+
+const mainStore = useMainStore()
+const snapshotStore = useSnapshotStore()
+const { databaseId } = storeToRefs(mainStore)
+const { screening } = storeToRefs(useScreenStore())
+
+if (import.meta.env.MODE !== 'development') {
+  window.onbeforeunload = () => false
+}
+
+onMounted(async () => {
+  await deleteDiscardedDB()
+  snapshotStore.initSnapshotDatabase()
+  mainStore.setAvailableFonts()
+})
+
+// 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
+window.addEventListener('unload', () => {
+  const discardedDB = localStorage.getItem(LOCALSTORAGE_KEY_DISCARDED_DB)
+  const discardedDBList: string[] = discardedDB ? JSON.parse(discardedDB) : []
+
+  discardedDBList.push(databaseId.value)
+
+  const newDiscardedDB = JSON.stringify(discardedDBList)
+  localStorage.setItem(LOCALSTORAGE_KEY_DISCARDED_DB, newDiscardedDB)
+})
+</script>
+
+<style lang="scss">
+#app {
+  height: 100%;
+}
+</style>

BIN
src/assets/fonts/仓耳小丸子.woff2


BIN
src/assets/fonts/优设标题黑.woff2


BIN
src/assets/fonts/字制区喜脉体.woff2


BIN
src/assets/fonts/峰广明锐体.woff2


BIN
src/assets/fonts/得意黑.woff2


BIN
src/assets/fonts/摄图摩登小方体.woff2


BIN
src/assets/fonts/站酷快乐体.woff2


BIN
src/assets/fonts/素材集市康康体.woff2


BIN
src/assets/fonts/素材集市酷方体.woff2


BIN
src/assets/fonts/途牛类圆体.woff2


BIN
src/assets/fonts/锐字真言体.woff2


+ 9 - 0
src/assets/styles/font.scss

@@ -0,0 +1,9 @@
+$fontList: '仓耳小丸子', '优设标题黑', '字制区喜脉体', '峰广明锐体', '得意黑', '摄图摩登小方体', '站酷快乐体', '素材集市康康体', '素材集市酷方体', '途牛类圆体', '锐字真言体';
+
+@each $font in $fontList {
+  @font-face {
+    font-display: swap;
+    font-family: $font;
+    src: url('../fonts/#{$font}.woff2') format('woff2');
+  }
+}

+ 138 - 0
src/assets/styles/global.scss

@@ -0,0 +1,138 @@
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font-size: 100%;
+  vertical-align: baseline;
+  box-sizing: border-box;
+}
+
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+  display: block;
+}
+
+html,
+body {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  background-color: #fff;
+  color: $textColor;
+}
+
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
+}
+
+ol,
+ul {
+  list-style: none;
+}
+
+blockquote, q {
+  quotes: none;
+}
+
+blockquote::before,
+blockquote::after,
+q::before,
+q::after {
+  content: '';
+}
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+a {
+  text-decoration: none;
+  color: $themeColor;
+}
+
+img {
+  vertical-align: middle;
+  border-style: none;
+}
+
+hr {
+  box-sizing: content-box;
+  height: 0;
+  overflow: visible;
+}
+
+mark.active {
+  background-color: #ff9632;
+}
+
+input,
+button,
+select,
+optgroup,
+textarea {
+  color: inherit;
+}
+
+button,
+input {
+  overflow: visible;
+}
+
+button,
+select {
+  text-transform: none;
+}
+
+textarea {
+  overflow: auto;
+  resize: vertical;
+}
+
+a,
+area,
+button,
+[role='button'],
+input:not([type='range']),
+label,
+select,
+summary,
+textarea {
+  touch-action: manipulation;
+}
+
+::-webkit-scrollbar {
+  width: 5px;
+  height: 5px;
+  background-color: transparent;
+}
+::-webkit-scrollbar-thumb {
+  background-color: #e1e1e1;
+  border-radius: 3px;
+}

+ 42 - 0
src/assets/styles/mixin.scss

@@ -0,0 +1,42 @@
+@mixin ellipsis-oneline() {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+
+@mixin ellipsis-multiline($line: 2) {
+  word-wrap: break-word;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: $line;
+  -webkit-box-orient: vertical;
+}
+
+@mixin flex-grid-layout() {
+  display: flex;
+  flex-wrap: wrap;
+  align-content: flex-start;
+}
+
+@mixin flex-grid-layout-children($col, $colWidth) {
+  width: $colWidth;
+  margin-bottom: calc(#{100 - $col * $colWidth} / #{$col - 1});
+
+  &:not(:nth-child(#{$col}n)) {
+    margin-right: calc(#{100 - $col * $colWidth} / #{$col - 1});
+  }
+}
+
+@mixin overflow-overlay() {
+  overflow: auto;
+  overflow: overlay;
+}
+
+@mixin absolute-0() {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+}

+ 99 - 0
src/assets/styles/prosemirror.scss

@@ -0,0 +1,99 @@
+.ProseMirror, .ProseMirror-static {
+  outline: 0;
+  border: 0;
+  font-size: 20px;
+  word-break: break-word;
+  white-space: normal;
+
+  &:not(.ProseMirror-static) {
+    user-select: text;
+  }
+
+  ::selection {
+    background-color: rgba($themeColor, 0.25);
+    color: inherit;
+  }
+
+  p {
+    margin-top: var(--paragraphSpace);
+  }
+  p:first-child {
+    margin-top: 0;
+  }
+
+  ul {
+    list-style-type: disc;
+    padding-inline-start: 1.25em;
+
+    li {
+      list-style-type: inherit;
+      padding: 2px 0;
+    }
+  }
+
+  ol {
+    list-style-type: decimal;
+    padding-inline-start: 1.25em;
+
+    li {
+      list-style-type: inherit;
+      padding: 2px 0;
+    }
+  }
+
+  code {
+    background-color: $borderColor;
+    padding: 2px 6px;
+    margin: 0 1px;
+    border-radius: 4px;
+    font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
+  }
+
+  sup {
+    vertical-align: super;
+    font-size: smaller;
+  }
+  sub {
+    vertical-align: sub;
+    font-size: smaller;
+  }
+
+  blockquote {
+    overflow: hidden;
+    padding-right: 1.2em;
+    padding-left: 1.2em;
+    margin-left: 0;
+    margin-right: 0;
+    font-style: italic;
+    border-left: 4px solid #ddd;
+  }
+
+  [data-indent='1'] {
+    padding-left: 20px;
+  }
+  [data-indent='2'] {
+    padding-left: 40px;
+  }
+  [data-indent='3'] {
+    padding-left: 60px;
+  }
+  [data-indent='4'] {
+    padding-left: 80px;
+  }
+  [data-indent='5'] {
+    padding-left: 100px;
+  }
+  [data-indent='6'] {
+    padding-left: 120px;
+  }
+  [data-indent='7'] {
+    padding-left: 140px;
+  }
+  [data-indent='8'] {
+    padding-left: 160px;
+  }
+}
+
+.ProseMirror-selectednode {
+  outline: none !important;
+}

+ 13 - 0
src/assets/styles/variable.scss

@@ -0,0 +1,13 @@
+$themeColor: #d14424;
+$themeHoverColor: #de6949;
+$textColor: #41464b;
+$borderColor: #e5e7eb;
+$lightGray: #f9f9f9;
+
+$boxShadow: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -2px rgba(0, 0, 0, .1);
+
+$transitionDelay: .2s;
+$transitionDelayFast: .1s;
+$transitionDelaySlow: .3s;
+
+$borderRadius: 2px;

+ 7 - 0
src/components.d.ts

@@ -0,0 +1,7 @@
+import type { Icons } from '@/plugins/icon'
+
+declare module 'vue' {
+  export type GlobalComponents = Icons
+}
+
+export {}

+ 116 - 0
src/components/Button.vue

@@ -0,0 +1,116 @@
+<template>
+  <button 
+    class="button"
+    :class="{
+      'disabled': disabled,
+      'checked': !disabled && checked,
+      'default': !disabled && type === 'default',
+      'primary': !disabled && type === 'primary',
+      'checkbox': !disabled && type === 'checkbox',
+      'radio': !disabled && type === 'radio',
+      'small': size === 'small',
+      'first': first,
+      'last': last,
+    }"
+    @click="handleClick()"
+  >
+    <slot></slot>
+  </button>
+</template>
+
+<script lang="ts" setup>
+const props = withDefaults(defineProps<{
+  checked?: boolean
+  disabled?: boolean
+  type?: 'default' | 'primary' | 'checkbox' | 'radio'
+  size?: 'small' | 'normal'
+  first?: boolean
+  last?: boolean
+}>(), {
+  checked: false,
+  disabled: false,
+  type: 'default',
+  size: 'normal',
+  first: false,
+  last: false,
+})
+
+const emit = defineEmits<{
+  (event: 'click'): void
+}>()
+
+const handleClick = () => {
+  if (props.disabled) return
+  emit('click')
+}
+</script>
+
+<style lang="scss" scoped>
+.button {
+  height: 32px;
+  line-height: 32px;
+  outline: 0;
+  font-size: 13px;
+  padding: 0 15px;
+  text-align: center;
+  color: $textColor;
+  border-radius: $borderRadius;
+  user-select: none;
+  letter-spacing: 1px;
+  cursor: pointer;
+
+  &.small {
+    height: 24px;
+    line-height: 24px;
+    padding: 0 7px;
+    letter-spacing: 0;
+    font-size: 12px;
+  }
+
+  &.default {
+    background-color: #fff;
+    border: 1px solid #d9d9d9;
+    color: $textColor;
+
+    &:hover {
+      color: $themeColor;
+      border-color: $themeColor;
+    }
+  }
+  &.primary {
+    background-color: $themeColor;
+    border: 1px solid $themeColor;
+    color: #fff;
+
+    &:hover {
+      background-color: $themeHoverColor;
+      border-color: $themeHoverColor;
+    }
+  }
+  &.checkbox, &.radio {
+    background-color: #fff;
+    border: 1px solid #d9d9d9;
+    color: $textColor;
+
+    &:not(.checked):hover {
+      color: $themeColor;
+    }
+  }
+  &.checked {
+    color: #fff;
+    background-color: $themeColor;
+    border-color: $themeColor;
+
+    &:hover {
+      background-color: $themeHoverColor;
+      border-color: $themeHoverColor;
+    }
+  }
+  &.disabled {
+    background-color: #f5f5f5;
+    border: 1px solid #d9d9d9;
+    color: #b7b7b7;
+    cursor: default;
+  }
+}
+</style>

+ 86 - 0
src/components/ButtonGroup.vue

@@ -0,0 +1,86 @@
+<template>
+  <div class="button-group" :class="{ 'passive': passive }" ref="groupRef">
+    <slot></slot>
+  </div>
+</template>
+
+<script lang="ts" setup>
+withDefaults(defineProps<{
+  passive?: boolean
+}>(), {
+  passive: false,
+})
+</script>
+
+<style lang="scss" scoped>
+.button-group {
+  display: flex;
+  align-items: center;
+
+  ::v-deep(button.button) {
+    border-radius: 0;
+    border-left-width: 1px;
+    border-right-width: 0;
+    display: inline-block;
+  }
+
+  &:not(.passive) {
+    ::v-deep(button.button) {
+      &:not(:last-child, .radio, .checkbox):hover {
+        position: relative;
+
+        &::after {
+          content: '';
+          width: 1px;
+          height: calc(100% + 2px);
+          background-color: $themeColor;
+          position: absolute;
+          top: -1px;
+          right: -1px;
+        }
+      }
+
+      &:first-child {
+        border-top-left-radius: $borderRadius;
+        border-bottom-left-radius: $borderRadius;
+        border-left-width: 1px;
+      }
+
+      &:last-child {
+        border-top-right-radius: $borderRadius;
+        border-bottom-right-radius: $borderRadius;
+        border-right-width: 1px;
+      }
+    }
+  }
+  &.passive {
+    ::v-deep(button.button) {
+      &:not(.last, .radio, .checkbox):hover {
+        position: relative;
+
+        &::after {
+          content: '';
+          width: 1px;
+          height: calc(100% + 2px);
+          background-color: $themeColor;
+          position: absolute;
+          top: -1px;
+          right: -1px;
+        }
+      }
+
+      &.first {
+        border-top-left-radius: $borderRadius;
+        border-bottom-left-radius: $borderRadius;
+        border-left-width: 1px;
+      }
+
+      &.last {
+        border-top-right-radius: $borderRadius;
+        border-bottom-right-radius: $borderRadius;
+        border-right-width: 1px;
+      }
+    }
+  }
+}
+</style>

+ 109 - 0
src/components/Checkbox.vue

@@ -0,0 +1,109 @@
+<template>
+  <label 
+    class="checkbox"
+    :class="{
+      'checked': value,
+      'disabled': disabled,
+    }"
+    @change="$event => handleChange($event)"
+  >
+    <span class="checkbox-input"></span>
+    <input class="checkbox-original" type="checkbox" :checked="value">
+    <span class="checkbox-label">
+      <slot></slot>
+    </span>
+  </label>
+</template>
+
+<script lang="ts" setup>
+const props = withDefaults(defineProps<{
+  value: boolean
+  disabled?: boolean
+}>(), {
+  disabled: false,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: boolean): void
+}>()
+
+const handleChange = (e: Event) => {
+  if (props.disabled) return
+  emit('update:value', (e.target as HTMLInputElement).checked)
+}
+</script>
+
+<style lang="scss" scoped>
+.checkbox {
+  height: 20px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+
+  &:not(.disabled).checked {
+    .checkbox-input {
+      background-color: $themeColor;
+      border-color: $themeColor;
+    }
+    .checkbox-input::after {
+      transform: rotate(45deg) scaleY(1);
+    }
+
+    .checkbox-label {
+      color: $themeColor;
+    }
+  }
+
+  &.disabled {
+    color: #b7b7b7;
+    cursor: default;
+
+    .checkbox-input {
+      background-color: #f5f5f5;
+    }
+  }
+}
+
+.checkbox-input {
+  display: inline-block;
+  position: relative;
+  border: 1px solid #d9d9d9;
+  border-radius: $borderRadius;
+  width: 16px;
+  height: 16px;
+  background-color: #fff;
+  vertical-align: middle;
+  transition: border-color .15s cubic-bezier(.71, -.46, .29, 1.46), background-color .15s cubic-bezier(.71, -.46, .29, 1.46);
+  z-index: 1;
+
+  &::after {
+    content: '';
+    border: 2px solid #fff;
+    border-left: 0;
+    border-top: 0;
+    height: 9px;
+    left: 4px;
+    position: absolute;
+    top: 1px;
+    transform: rotate(45deg) scaleY(0);
+    width: 6px;
+    transition: transform .15s ease-in .05s;
+    transform-origin: center;
+  }
+}
+.checkbox-original {
+  opacity: 0;
+  outline: 0;
+  position: absolute;
+  margin: 0;
+  width: 0;
+  height: 0;
+  z-index: -1;
+}
+.checkbox-label {
+  margin-left: 5px;
+  line-height: 20px;
+  font-size: 13px;
+  user-select: none;
+}
+</style>

+ 21 - 0
src/components/CheckboxButton.vue

@@ -0,0 +1,21 @@
+<template>
+  <Button 
+    :checked="checked"
+    :disabled="disabled"
+    type="checkbox"
+  >
+    <slot></slot>
+  </Button>
+</template>
+
+<script lang="ts" setup>
+import Button from './Button.vue'
+
+withDefaults(defineProps<{
+  checked?: boolean
+  disabled?: boolean
+}>(), {
+  checked: false,
+  disabled: false,
+})
+</script>

+ 42 - 0
src/components/ColorButton.vue

@@ -0,0 +1,42 @@
+<template>
+  <Button class="color-btn">
+    <div class="color-block">
+      <div class="content" :style="{ backgroundColor: color }"></div>
+    </div>
+    <IconPlatte class="color-btn-icon" />
+  </Button>
+</template>
+
+<script lang="ts" setup>
+import Button from './Button.vue'
+
+defineProps<{
+  color: string
+}>()
+</script>
+
+<style lang="scss" scoped>
+.color-btn {
+  width: 100%;
+  display: flex !important;
+  align-items: center;
+  justify-content: center;
+  padding: 0 !important;
+}
+.color-block {
+  height: 20px;
+  margin-left: 8px;
+  flex: 1;
+  outline: 1px dashed rgba($color: #666, $alpha: .12);
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAEBJREFUOE9jfPbs2X8GIoCkpCQRqhgYGEcNxBlOo2GIM2iGQLL5//8/UTnl+fPnxOWUUQNxhtNoGOLOKYM+2QAAh2Nq10DwkukAAAAASUVORK5CYII=);
+}
+.content {
+  width: 100%;
+  height: 100%;
+}
+.color-btn-icon {
+  width: 32px;
+  font-size: 13px;
+  color: #bfbfbf;
+}
+</style>

+ 107 - 0
src/components/ColorPicker/Alpha.vue

@@ -0,0 +1,107 @@
+<template>
+  <div class="alpha">
+    <div class="alpha-checkboard-wrap">
+      <Checkboard />
+    </div>
+    <div class="alpha-gradient" :style="{ background: gradientColor }"></div>
+    <div 
+      class="alpha-container" 
+      ref="alphaRef"
+      @mousedown="$event => handleMouseDown($event)"
+    >
+      <div class="alpha-pointer" :style="{ left: color.a * 100 + '%' }">
+        <div class="alpha-picker"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onUnmounted, ref } from 'vue'
+
+import Checkboard from './Checkboard.vue'
+import type { ColorFormats } from 'tinycolor2'
+
+const props = defineProps<{
+  value: ColorFormats.RGBA
+}>()
+
+const emit = defineEmits<{
+  (event: 'colorChange', payload: ColorFormats.RGBA): void
+}>()
+
+const color = computed(() => props.value)
+    
+const gradientColor = computed(() => {
+  const rgbaStr = [color.value.r, color.value.g, color.value.b].join(',')
+  return `linear-gradient(to right, rgba(${rgbaStr}, 0) 0%, rgba(${rgbaStr}, 1) 100%)`
+})
+
+const alphaRef = ref<HTMLElement>()
+const handleChange = (e: MouseEvent) => {
+  e.preventDefault()
+  if (!alphaRef.value) return
+  const containerWidth = alphaRef.value.clientWidth
+  const xOffset = alphaRef.value.getBoundingClientRect().left + window.pageXOffset
+  const left = e.pageX - xOffset
+  let a
+
+  if (left < 0) a = 0
+  else if (left > containerWidth) a = 1
+  else a = Math.round(left * 100 / containerWidth) / 100
+
+  if (color.value.a !== a) {
+    emit('colorChange', {
+      r: color.value.r,
+      g: color.value.g,
+      b: color.value.b,
+      a: a,
+    })
+  }
+}
+
+const unbindEventListeners = () => {
+  window.removeEventListener('mousemove', handleChange)
+  window.removeEventListener('mouseup', unbindEventListeners)
+}
+const handleMouseDown = (e: MouseEvent) => {
+  handleChange(e)
+  window.addEventListener('mousemove', handleChange)
+  window.addEventListener('mouseup', unbindEventListeners)
+}
+onUnmounted(unbindEventListeners)
+</script>
+
+<style lang="scss" scoped>
+.alpha {
+  @include absolute-0();
+}
+.alpha-checkboard-wrap {
+  overflow: hidden;
+
+  @include absolute-0();
+}
+.alpha-gradient {
+  @include absolute-0();
+}
+.alpha-container {
+  cursor: pointer;
+  position: relative;
+  z-index: 2;
+  height: 100%;
+  margin: 0 3px;
+}
+.alpha-pointer {
+  z-index: 2;
+  position: absolute;
+}
+.alpha-picker {
+  cursor: pointer;
+  width: 4px;
+  height: 8px;
+  box-shadow: 0 0 2px rgba(0, 0, 0, .6);
+  background: #fff;
+  margin-top: 1px;
+  transform: translateX(-2px);
+}
+</style>

+ 60 - 0
src/components/ColorPicker/Checkboard.vue

@@ -0,0 +1,60 @@
+<template>
+  <div class="checkerboard" :style="bgStyle"></div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+
+const props = withDefaults(defineProps<{
+  size?: number
+  white?: string
+  grey?: string
+}>(), {
+  size: 8,
+  white: '#fff',
+  grey: '#e6e6e6',
+})
+
+interface CheckboardCache {
+  [key: string]: string | null
+}
+const checkboardCache: CheckboardCache = {}
+
+const renderCheckboard = (white: string, grey: string, size: number) => {
+  const canvas = document.createElement('canvas')
+  canvas.width = canvas.height = size * 2
+  const ctx = canvas.getContext('2d')
+  
+  if (!ctx) return null
+
+  ctx.fillStyle = white
+  ctx.fillRect(0, 0, canvas.width, canvas.height)
+  ctx.fillStyle = grey
+  ctx.fillRect(0, 0, size, size)
+  ctx.translate(size, size)
+  ctx.fillRect(0, 0, size, size)
+  return canvas.toDataURL()
+}
+
+const getCheckboard = (white: string, grey: string, size: number) => {
+  const key = white + ',' + grey + ',' + size
+  if (checkboardCache[key]) return checkboardCache[key]
+  
+  const checkboard = renderCheckboard(white, grey, size)
+  checkboardCache[key] = checkboard
+  return checkboard
+}
+
+const bgStyle = computed(() => {
+  const checkboard = getCheckboard(props.white, props.grey, props.size)
+  return { backgroundImage: `url(${checkboard})` }
+})
+</script>
+
+<style lang="scss" scoped>
+.checkerboard {
+  background-size: contain;
+
+  @include absolute-0();
+}
+</style>

+ 69 - 0
src/components/ColorPicker/EditableInput.vue

@@ -0,0 +1,69 @@
+<template>
+  <div class="editable-input">
+    <input
+      class="input-content"
+      :value="val"
+      @input="$event => handleInput($event)"
+    >
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import tinycolor, { type ColorFormats } from 'tinycolor2'
+
+const props = defineProps<{
+  value: ColorFormats.RGBA
+}>()
+
+const emit = defineEmits<{
+  (event: 'colorChange', payload: ColorFormats.RGBA): void
+}>()
+
+const val = computed(() => {
+  let _hex = ''
+  if (props.value.a < 1) _hex = tinycolor(props.value).toHex8String().toUpperCase()
+  else _hex = tinycolor(props.value).toHexString().toUpperCase()
+  return _hex.replace('#', '')
+})
+
+const handleInput = (e: Event) => {
+  const value = (e.target as HTMLInputElement).value
+  if (value.length >= 6) {
+    const color = tinycolor(value)
+    if (color.isValid()) {
+      emit('colorChange', color.toRgb())
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.editable-input {
+  width: 100%;
+  position: relative;
+  overflow: hidden;
+  text-align: center;
+  font-size: 14px;
+
+  &::after {
+    content: '#';
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    color: #999;
+  }
+}
+.input-content {
+  width: 100%;
+  padding: 3px;
+  border: 0;
+  border-bottom: 1px solid #ddd;
+  outline: none;
+  text-align: center;
+}
+.input-label {
+  text-transform: capitalize;
+}
+</style>

+ 117 - 0
src/components/ColorPicker/Hue.vue

@@ -0,0 +1,117 @@
+<template>
+  <div class="hue">
+    <div 
+      class="hue-container"
+      ref="hueRef"
+      @mousedown="$event => handleMouseDown($event)"
+    >
+      <div 
+        class="hue-pointer"
+        :style="{ left: pointerLeft }"
+      >
+        <div class="hue-picker"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onUnmounted, ref, watch } from 'vue'
+import tinycolor, { type ColorFormats } from 'tinycolor2'
+
+const props = defineProps<{
+  value: ColorFormats.RGBA
+  hue: number
+}>()
+
+const emit = defineEmits<{
+  (event: 'colorChange', payload: ColorFormats.HSLA): void
+}>()
+
+const oldHue = ref(0)
+const pullDirection = ref('')
+
+const color = computed(() => {
+  const hsla = tinycolor(props.value).toHsl()
+  if (props.hue !== -1) hsla.h = props.hue
+  return hsla
+})
+
+const pointerLeft = computed(() => {
+  if (color.value.h === 0 && pullDirection.value === 'right') return '100%'
+  return color.value.h * 100 / 360 + '%'
+})
+
+watch(() => props.value, () => {
+  const hsla = tinycolor(props.value).toHsl()
+  const h = hsla.s === 0 ? props.hue : hsla.h
+  if (h !== 0 && h - oldHue.value > 0) pullDirection.value = 'right'
+  if (h !== 0 && h - oldHue.value < 0) pullDirection.value = 'left'
+  oldHue.value = h
+})
+
+const hueRef = ref<HTMLElement>()
+const handleChange = (e: MouseEvent) => {
+  e.preventDefault()
+  if (!hueRef.value) return
+
+  const containerWidth = hueRef.value.clientWidth
+  const xOffset = hueRef.value.getBoundingClientRect().left + window.pageXOffset
+  const left = e.pageX - xOffset
+  let h, percent
+  
+  if (left < 0) h = 0
+  else if (left > containerWidth) h = 360
+  else {
+    percent = left * 100 / containerWidth
+    h = 360 * percent / 100
+  }
+  if (props.hue === -1 || color.value.h !== h) {
+    emit('colorChange', {
+      h,
+      l: color.value.l,
+      s: color.value.s,
+      a: color.value.a,
+    })
+  }
+}
+
+const unbindEventListeners = () => {
+  window.removeEventListener('mousemove', handleChange)
+  window.removeEventListener('mouseup', unbindEventListeners)
+}
+const handleMouseDown = (e: MouseEvent) => {
+  handleChange(e)
+  window.addEventListener('mousemove', handleChange)
+  window.addEventListener('mouseup', unbindEventListeners)
+}
+onUnmounted(unbindEventListeners)
+</script>
+
+<style lang="scss" scoped>
+.hue {
+  background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
+
+  @include absolute-0();
+}
+.hue-container {
+  cursor: pointer;
+  margin: 0 2px;
+  position: relative;
+  height: 100%;
+}
+.hue-pointer {
+  z-index: 2;
+  position: absolute;
+  top: 0;
+}
+.hue-picker {
+  cursor: pointer;
+  margin-top: 1px;
+  width: 4px;
+  height: 8px;
+  box-shadow: 0 0 2px rgba(0, 0, 0, .6);
+  background: #fff;
+  transform: translateX(-2px);
+}
+</style>

+ 108 - 0
src/components/ColorPicker/Saturation.vue

@@ -0,0 +1,108 @@
+<template>
+  <div 
+    class="saturation"
+    ref="saturationRef"
+    :style="{ background: bgColor }"
+    @mousedown="$event => handleMouseDown($event)"
+  >
+    <div class="saturation-white"></div>
+    <div class="saturation-black"></div>
+    <div class="saturation-pointer" 
+      :style="{
+        top: pointerTop,
+        left: pointerLeft,
+      }"
+    >
+      <div class="saturation-circle"></div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onUnmounted, ref } from 'vue'
+import tinycolor, { type ColorFormats } from 'tinycolor2'
+import { throttle, clamp } from 'lodash'
+
+const props = defineProps<{
+  value: ColorFormats.RGBA
+  hue: number
+}>()
+
+const emit = defineEmits<{
+  (event: 'colorChange', payload: ColorFormats.HSVA): void
+}>()
+
+const color = computed(() => {
+  const hsva = tinycolor(props.value).toHsv()
+  if (props.hue !== -1) hsva.h = props.hue
+  return hsva
+})
+
+const bgColor = computed(() => `hsl(${color.value.h}, 100%, 50%)`)
+const pointerTop = computed(() => (-(color.value.v * 100) + 1) + 100 + '%')
+const pointerLeft = computed(() => color.value.s * 100 + '%')
+
+const emitChangeEvent = throttle(function(param: ColorFormats.HSVA) {
+  emit('colorChange', param)
+}, 20, { leading: true, trailing: false })
+
+const saturationRef = ref<HTMLElement>()
+const handleChange = (e: MouseEvent) => {
+  e.preventDefault()
+  if (!saturationRef.value) return
+  
+  const containerWidth = saturationRef.value.clientWidth
+  const containerHeight = saturationRef.value.clientHeight
+  const xOffset = saturationRef.value.getBoundingClientRect().left + window.pageXOffset
+  const yOffset = saturationRef.value.getBoundingClientRect().top + window.pageYOffset
+  const left = clamp(e.pageX - xOffset, 0, containerWidth)
+  const top = clamp(e.pageY - yOffset, 0, containerHeight)
+  const saturation = left / containerWidth
+  const bright = clamp(-(top / containerHeight) + 1, 0, 1)
+
+  emitChangeEvent({
+    h: color.value.h,
+    s: saturation,
+    v: bright,
+    a: color.value.a,
+  })
+}
+
+const unbindEventListeners = () => {
+  window.removeEventListener('mousemove', handleChange)
+  window.removeEventListener('mouseup', unbindEventListeners)
+}
+const handleMouseDown = (e: MouseEvent) => {
+  handleChange(e)
+  window.addEventListener('mousemove', handleChange)
+  window.addEventListener('mouseup', unbindEventListeners)
+}
+onUnmounted(unbindEventListeners)
+</script>
+
+<style lang="scss" scoped>
+.saturation,
+.saturation-white,
+.saturation-black {
+  @include absolute-0();
+
+  cursor: pointer;
+}
+.saturation-white {
+  background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
+}
+.saturation-black {
+  background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
+}
+.saturation-pointer {
+  cursor: pointer;
+  position: absolute;
+}
+.saturation-circle {
+  width: 4px;
+  height: 4px;
+  box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0, 0, 0, .3), 0 0 1px 2px rgba(0, 0, 0, .4);
+  border-radius: 50%;
+  transform: translate(-2px, -2px);
+}
+</style>

+ 443 - 0
src/components/ColorPicker/index.vue

@@ -0,0 +1,443 @@
+<template>
+  <div class="color-picker">
+    <div class="picker-saturation-wrap">
+      <Saturation :value="color" :hue="hue" @colorChange="value => changeColor(value)" />
+    </div>
+    <div class="picker-controls">
+      <div class="picker-color-wrap">
+        <div class="picker-current-color" :style="{ background: currentColor }"></div>
+        <Checkboard />
+      </div>
+      <div class="picker-sliders">
+        <div class="picker-hue-wrap">
+          <Hue :value="color" :hue="hue" @colorChange="value => changeColor(value)" />
+        </div>
+        <div class="picker-alpha-wrap">
+          <Alpha :value="color" @colorChange="value => changeColor(value)" />
+        </div>
+      </div>
+    </div>
+
+    <div class="picker-field">
+      <EditableInput class="input" :value="color" @colorChange="value => changeColor(value)" />
+      <div class="straw" @click="openEyeDropper()"><IconNeedle /></div>
+      <div class="transparent" @click="selectPresetColor('#00000000')">
+        <Checkboard />
+      </div>
+    </div>
+
+    <div class="picker-presets">
+      <div
+        class="picker-presets-color"
+        v-for="c in themeColors"
+        :key="c"
+        :style="{ background: c }"
+        @click="selectPresetColor(c)"
+      ></div>
+    </div>
+
+    <div class="picker-gradient-presets">
+      <div
+        class="picker-gradient-col"
+        v-for="(col, index) in presetColors"
+        :key="index"
+      >
+        <div class="picker-gradient-color"
+          v-for="c in col"
+          :key="c"
+          :style="{ background: c }"
+          @click="selectPresetColor(c)"
+        ></div>
+      </div>
+    </div>
+
+    <div class="picker-presets">
+      <div
+        v-for="c in standardColors"
+        :key="c"
+        class="picker-presets-color"
+        :style="{ background: c }"
+        @click="selectPresetColor(c)"
+      ></div>
+    </div>
+
+    <div class="recent-colors-title" v-if="recentColors.length">最近使用:</div>
+    <div class="picker-presets">
+      <div
+        v-for="c in recentColors"
+        :key="c"
+        class="picker-presets-color alpha"
+        @click="selectPresetColor(c)"
+      >
+        <div class="picker-presets-color-content" :style="{ background: c }"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref, watch } from 'vue'
+import tinycolor, { type ColorFormats } from 'tinycolor2'
+import { debounce } from 'lodash'
+import { toCanvas } from 'html-to-image'
+import message from '@/utils/message'
+
+import Alpha from './Alpha.vue'
+import Checkboard from './Checkboard.vue'
+import Hue from './Hue.vue'
+import Saturation from './Saturation.vue'
+import EditableInput from './EditableInput.vue'
+
+const props = withDefaults(defineProps<{
+  modelValue?: string
+}>(), {
+  modelValue: '#e86b99',
+})
+
+const emit = defineEmits<{
+  (event: 'update:modelValue', payload: string): void
+}>()
+
+const RECENT_COLORS = 'RECENT_COLORS'
+
+const presetColorConfig = [
+  ['#7f7f7f', '#f2f2f2'],
+  ['#0d0d0d', '#808080'],
+  ['#1c1a10', '#ddd8c3'],
+  ['#0e243d', '#c6d9f0'],
+  ['#233f5e', '#dae5f0'],
+  ['#632623', '#f2dbdb'],
+  ['#4d602c', '#eaf1de'],
+  ['#3f3150', '#e6e0ec'],
+  ['#1e5867', '#d9eef3'],
+  ['#99490f', '#fee9da'],
+]
+
+const gradient = (startColor: string, endColor: string, step: number) => {
+  const _startColor = tinycolor(startColor).toRgb()
+  const _endColor = tinycolor(endColor).toRgb()
+
+  const rStep = (_endColor.r - _startColor.r) / step
+  const gStep = (_endColor.g - _startColor.g) / step
+  const bStep = (_endColor.b - _startColor.b) / step
+  const gradientColorArr = []
+
+  for (let i = 0; i < step; i++) {
+    const gradientColor = tinycolor({
+      r: _startColor.r + rStep * i,
+      g: _startColor.g + gStep * i,
+      b: _startColor.b + bStep * i,
+    }).toRgbString()
+    gradientColorArr.push(gradientColor)
+  }
+  return gradientColorArr
+}
+
+const getPresetColors = () => {
+  const presetColors = []
+  for (const color of presetColorConfig) {
+    presetColors.push(gradient(color[1], color[0], 5))
+  }
+  return presetColors
+}
+
+const themeColors = ['#000000', '#ffffff', '#eeece1', '#1e497b', '#4e81bb', '#e2534d', '#9aba60', '#8165a0', '#47acc5', '#f9974c']
+const standardColors = ['#c21401', '#ff1e02', '#ffc12a', '#ffff3a', '#90cf5b', '#00af57', '#00afee', '#0071be', '#00215f', '#72349d']
+
+const hue = ref(-1)
+const recentColors = ref<string[]>([])
+
+const color = computed({
+  get() {
+    return tinycolor(props.modelValue).toRgb()
+  },
+  set(rgba: ColorFormats.RGBA) {
+    const rgbaString = `rgba(${[rgba.r, rgba.g, rgba.b, rgba.a].join(',')})`
+    emit('update:modelValue', rgbaString)
+  },
+})
+
+const presetColors = getPresetColors()
+
+const currentColor = computed(() => {
+  return `rgba(${[color.value.r, color.value.g, color.value.b, color.value.a].join(',')})`
+})
+
+const selectPresetColor = (colorString: string) => {
+  hue.value = tinycolor(colorString).toHsl().h
+  emit('update:modelValue', colorString)
+}
+
+// 每次选择非预设颜色时,需要将该颜色加入到最近使用列表中
+const updateRecentColorsCache = debounce(function() {
+  const _color = tinycolor(color.value).toRgbString()
+  if (!recentColors.value.includes(_color)) {
+    recentColors.value = [_color, ...recentColors.value]
+
+    const maxLength = 10
+    if (recentColors.value.length > maxLength) {
+      recentColors.value = recentColors.value.slice(0, maxLength)
+    }
+  }
+}, 300, { trailing: true })
+
+onMounted(() => {
+  const recentColorsCache = localStorage.getItem(RECENT_COLORS)
+  if (recentColorsCache) recentColors.value = JSON.parse(recentColorsCache)
+})
+
+watch(recentColors, () => {
+  const recentColorsCache = JSON.stringify(recentColors.value)
+  localStorage.setItem(RECENT_COLORS, recentColorsCache)
+})
+
+const changeColor = (value: ColorFormats.RGBA | ColorFormats.HSLA | ColorFormats.HSVA) => {
+  if ('h' in value) {
+    hue.value = value.h
+    color.value = tinycolor(value).toRgb()
+  }
+  else {
+    hue.value = tinycolor(value).toHsl().h
+    color.value = value
+  }
+
+  updateRecentColorsCache()
+}
+
+// 打开取色吸管
+// 检查环境是否支持原生取色吸管,支持则使用原生吸管,否则使用自定义吸管
+const openEyeDropper = () => {
+  const isSupportedEyeDropper = 'EyeDropper' in window
+
+  if (isSupportedEyeDropper) browserEyeDropper()
+  else customEyeDropper()
+}
+
+// 原生取色吸管
+const browserEyeDropper = () => {
+  message.success('按 ESC 键关闭取色吸管', { duration: 0 })
+
+  // eslint-disable-next-line
+  const eyeDropper = new (window as any).EyeDropper()
+  eyeDropper.open().then((result: { sRGBHex: string }) => {
+    const tColor = tinycolor(result.sRGBHex)
+    hue.value = tColor.toHsl().h
+    color.value = tColor.toRgb()
+
+    message.closeAll()
+    updateRecentColorsCache()
+  }).catch(() => {
+    message.closeAll()
+  })
+}
+
+// 基于 Canvas 的自定义取色吸管
+const customEyeDropper = () => {
+  const targetRef: HTMLElement | null = document.querySelector('.canvas')
+  if (!targetRef) return
+
+  const maskRef = document.createElement('div')
+  maskRef.style.cssText = 'position: fixed; top: 0; left: 0; bottom: 0; right: 0; z-index: 9999; cursor: wait;'
+  document.body.appendChild(maskRef)
+
+  const colorBlockRef = document.createElement('div')
+  colorBlockRef.style.cssText = 'position: absolute; top: -100px; left: -100px; width: 16px; height: 16px; border: 1px solid #000; z-index: 999'
+  maskRef.appendChild(colorBlockRef)
+
+  const { left, top, width, height } = targetRef.getBoundingClientRect()
+
+  const filter = (node: HTMLElement) => {
+    if (node.tagName && node.tagName.toUpperCase() === 'FOREIGNOBJECT') return false
+    if (node.classList && node.classList.contains('operate')) return false
+    return true
+  }
+
+  toCanvas(targetRef, { filter, fontEmbedCSS: '', width, height, canvasWidth: width, canvasHeight: height, pixelRatio: 1 }).then(canvasRef => {
+    canvasRef.style.cssText = `position: absolute; top: ${top}px; left: ${left}px; cursor: crosshair;`
+    maskRef.style.cursor = 'default'
+    maskRef.appendChild(canvasRef)
+
+    const ctx = canvasRef.getContext('2d')
+    if (!ctx) return
+
+    let currentColor = ''
+    const handleMousemove = (e: MouseEvent) => {
+      const x = e.x
+      const y = e.y
+
+      const mouseX = x - left
+      const mouseY = y - top
+
+      const [r, g, b, a] = ctx.getImageData(mouseX, mouseY, 1, 1).data
+      currentColor = `rgba(${r}, ${g}, ${b}, ${(a / 255).toFixed(2)})`
+
+      colorBlockRef.style.left = x + 10 + 'px'
+      colorBlockRef.style.top = y + 10 + 'px'
+      colorBlockRef.style.backgroundColor = currentColor
+    }
+    const handleMouseleave = () => {
+      currentColor = ''
+      colorBlockRef.style.left = '-100px'
+      colorBlockRef.style.top = '-100px'
+      colorBlockRef.style.backgroundColor = ''
+    }
+    const handleMousedown = (e: MouseEvent) => {
+      if (currentColor && e.button === 0) {
+        const tColor = tinycolor(currentColor)
+        hue.value = tColor.toHsl().h
+        color.value = tColor.toRgb()
+
+        updateRecentColorsCache()
+      }
+      document.body.removeChild(maskRef)
+      
+      canvasRef.removeEventListener('mousemove', handleMousemove)
+      canvasRef.removeEventListener('mouseleave', handleMouseleave)
+      window.removeEventListener('mousedown', handleMousedown)
+    }
+
+    canvasRef.addEventListener('mousemove', handleMousemove)
+    canvasRef.addEventListener('mouseleave', handleMouseleave)
+    window.addEventListener('mousedown', handleMousedown)
+  }).catch(() => {
+    message.error('取色吸管初始化失败')
+    document.body.removeChild(maskRef)
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.color-picker {
+  position: relative;
+  width: 240px;
+  background: #fff;
+  user-select: none;
+  margin-bottom: -10px;
+}
+.picker-saturation-wrap {
+  width: 100%;
+  padding-bottom: 50%;
+  position: relative;
+  overflow: hidden;
+}
+.picker-controls {
+  display: flex;
+}
+.picker-sliders {
+  padding: 4px 0;
+  flex: 1;
+}
+.picker-hue-wrap {
+  position: relative;
+  height: 10px;
+}
+.picker-alpha-wrap {
+  position: relative;
+  height: 10px;
+  margin-top: 4px;
+  overflow: hidden;
+}
+.picker-color-wrap {
+  width: 24px;
+  height: 24px;
+  position: relative;
+  margin-top: 4px;
+  margin-right: 4px;
+  outline: 1px dashed rgba($color: #666, $alpha: .12);
+
+  .checkerboard {
+    background-size: auto;
+  }
+}
+.picker-current-color {
+  @include absolute-0();
+
+  z-index: 2;
+}
+
+.picker-field {
+  display: flex;
+  margin-bottom: 8px;
+
+  .transparent {
+    width: 24px;
+    height: 24px;
+    margin-top: 4px;
+    margin-left: 8px;
+    position: relative;
+    cursor: pointer;
+
+    &::after {
+      content: '';
+      width: 26px;
+      height: 2px;
+      position: absolute;
+      top: 11px;
+      left: -1px;
+      transform: rotate(-45deg);
+      background-color: #f00;
+    }
+
+    .checkerboard {
+      background-size: auto;
+    }
+  }
+
+  .straw {
+    width: 24px;
+    height: 24px;
+    margin-top: 4px;
+    margin-left: 8px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 20px;
+    background-color: #f5f5f5;
+    outline: 1px solid #f1f1f1;
+    cursor: pointer;
+  }
+  .input {
+    flex: 1;
+  }
+}
+
+.picker-presets {
+  @include flex-grid-layout();
+}
+.picker-presets-color {
+  @include flex-grid-layout-children(10, 7%);
+
+  height: 0;
+  padding-bottom: 7%;
+  flex-shrink: 0;
+  position: relative;
+  cursor: pointer;
+
+  &.alpha {
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAADBJREFUOE9jfPbs2X8GPEBSUhKfNAPjqAHDIgz+//+PNx08f/4cfzoYNYCBceiHAQC5flV5JzgrxQAAAABJRU5ErkJggg==);
+  }
+}
+.picker-presets-color-content {
+  @include absolute-0();
+}
+.picker-gradient-presets {
+  @include flex-grid-layout();
+}
+.picker-gradient-col {
+  @include flex-grid-layout-children(10, 7%);
+
+  display: flex;
+  flex-direction: column;
+}
+.picker-gradient-color {
+  width: 100%;
+  height: 16px;
+  position: relative;
+  cursor: pointer;
+}
+
+.recent-colors-title {
+  font-size: 12px;
+  margin-bottom: 4px;
+}
+</style>

+ 137 - 0
src/components/Contextmenu/MenuContent.vue

@@ -0,0 +1,137 @@
+<template>
+  <ul class="menu-content">
+    <template v-for="(menu, index) in menus" :key="menu.text || index">
+      <li
+        v-if="!menu.hide"
+        class="menu-item"
+        @click.stop="handleClickMenuItem(menu)"
+        :class="{'divider': menu.divider, 'disable': menu.disable}"
+      >
+        <div 
+          class="menu-item-content" 
+          :class="{
+            'has-children': menu.children,
+            'has-handler': menu.handler,
+          }" 
+          v-if="!menu.divider"
+        >
+          <span class="text">{{menu.text}}</span>
+          <span class="sub-text" v-if="menu.subText && !menu.children">{{menu.subText}}</span>
+
+          <menu-content 
+            class="sub-menu"
+            :menus="menu.children" 
+            v-if="menu.children && menu.children.length"
+            :handleClickMenuItem="handleClickMenuItem" 
+          />
+        </div>
+      </li>
+    </template>
+  </ul>
+</template>
+
+<script lang="ts" setup>
+import type { ContextmenuItem } from './types'
+
+defineProps<{
+  menus: ContextmenuItem[]
+  handleClickMenuItem: (item: ContextmenuItem) => void
+}>()
+</script>
+
+<style lang="scss" scoped>
+$menuWidth: 180px;
+$menuHeight: 30px;
+$subMenuWidth: 120px;
+
+.menu-content {
+  width: $menuWidth;
+  padding: 5px 0;
+  background: #fff;
+  border: 1px solid $borderColor;
+  box-shadow: $boxShadow;
+  border-radius: $borderRadius;
+  list-style: none;
+  margin: 0;
+}
+.menu-item {
+  padding: 0 20px;
+  color: #555;
+  font-size: 12px;
+  transition: all $transitionDelayFast;
+  white-space: nowrap;
+  height: $menuHeight;
+  line-height: $menuHeight;
+  background-color: #fff;
+  cursor: pointer;
+
+  &:not(.disable):hover > .menu-item-content > .sub-menu {
+    display: block;
+  }
+
+  &:not(.disable):hover > .has-children.has-handler::after {
+    transform: scale(1);
+  }
+
+  &:hover:not(.disable) {
+    background-color: rgba($color: $themeColor, $alpha: .2);
+  }
+
+  &.divider {
+    height: 1px;
+    overflow: hidden;
+    margin: 5px;
+    background-color: #e5e5e5;
+    line-height: 0;
+    padding: 0;
+  }
+
+  &.disable {
+    color: #b1b1b1;
+    cursor: no-drop;
+  }
+}
+.menu-item-content {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  position: relative;
+
+  &.has-children::before {
+    content: '';
+    display: inline-block;
+    width: 8px;
+    height: 8px;
+    border-width: 1px;
+    border-style: solid;
+    border-color: #666 #666 transparent transparent;
+    position: absolute;
+    right: 0;
+    top: 50%;
+    transform: translateY(-50%) rotate(45deg);
+  }
+  &.has-children.has-handler::after {
+    content: '';
+    display: inline-block;
+    width: 1px;
+    height: 24px;
+    background-color: rgba($color: #fff, $alpha: .3);
+    position: absolute;
+    right: 18px;
+    top: 3px;
+    transform: scale(0);
+    transition: transform $transitionDelay;
+  }
+
+  .sub-text {
+    opacity: 0.6;
+  }
+  .sub-menu {
+    width: $subMenuWidth;
+    position: absolute;
+    display: none;
+    left: 112%;
+    top: -6px;
+  }
+}
+</style>

+ 80 - 0
src/components/Contextmenu/index.vue

@@ -0,0 +1,80 @@
+<template>
+  <div 
+    class="mask"
+    @contextmenu.prevent="removeContextmenu()"
+    @mousedown.left="removeContextmenu()"
+  ></div>
+
+  <div 
+    class="contextmenu"
+    :style="{
+      left: style.left + 'px',
+      top: style.top + 'px',
+    }"
+    @contextmenu.prevent
+  >
+    <MenuContent 
+      :menus="menus"
+      :handleClickMenuItem="handleClickMenuItem" 
+    />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import type { ContextmenuItem, Axis } from './types'
+
+import MenuContent from './MenuContent.vue'
+
+const props = defineProps<{
+  axis: Axis
+  el: HTMLElement
+  menus: ContextmenuItem[]
+  removeContextmenu: () => void
+}>()
+
+const style = computed(() => {
+  const MENU_WIDTH = 180
+  const MENU_HEIGHT = 30
+  const DIVIDER_HEIGHT = 11
+  const PADDING = 5
+
+  const { x, y } = props.axis
+  const menuCount = props.menus.filter(menu => !(menu.divider || menu.hide)).length
+  const dividerCount = props.menus.filter(menu => menu.divider).length
+
+  const menuWidth = MENU_WIDTH
+  const menuHeight = menuCount * MENU_HEIGHT + dividerCount * DIVIDER_HEIGHT + PADDING * 2
+
+  const screenWidth = document.body.clientWidth
+  const screenHeight = document.body.clientHeight
+
+  return {
+    left: screenWidth <= x + menuWidth ? x - menuWidth : x,
+    top: screenHeight <= y + menuHeight ? y - menuHeight : y,
+  }
+})
+
+const handleClickMenuItem = (item: ContextmenuItem) => {
+  if (item.disable) return
+  if (item.children && !item.handler) return
+  if (item.handler) item.handler(props.el)
+  props.removeContextmenu()
+}
+</script>
+
+<style lang="scss">
+.mask {
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 9998;
+}
+.contextmenu {
+  position: fixed;
+  z-index: 9999;
+  user-select: none;
+}
+</style>

+ 14 - 0
src/components/Contextmenu/types.ts

@@ -0,0 +1,14 @@
+export interface ContextmenuItem {
+  text?: string
+  subText?: string
+  divider?: boolean
+  disable?: boolean
+  hide?: boolean
+  children?: ContextmenuItem[]
+  handler?: (el: HTMLElement) => void
+}
+
+export interface Axis {
+  x: number
+  y: number
+}

+ 34 - 0
src/components/Divider.vue

@@ -0,0 +1,34 @@
+<template>
+  <div :class="['divider', type]"
+    :style="{
+      margin: type === 'horizontal' ? `${margin >= 0 ? margin : 24}px 0` : `0 ${margin >= 0 ? margin : 8}px`
+    }"
+  ></div>
+</template>
+
+<script lang="ts" setup>
+withDefaults(defineProps<{
+  type?: 'horizontal' | 'vertical'
+  margin?: number
+}>(), {
+  type: 'horizontal',
+  margin: -1,
+})
+</script>
+
+<style lang="scss" scoped>
+.divider {
+  &.horizontal {
+    width: 100%;
+    margin: 24px 0;
+    border-block-start: 1px solid rgba(5, 5, 5, .06);
+  }
+  &.vertical {
+    position: relative;
+    height: 1em;
+    display: inline-block;
+    margin: 0 8px;
+    border-inline-start: 1px solid rgba(5, 5, 5, .06);
+  }
+}
+</style>

+ 126 - 0
src/components/Drawer.vue

@@ -0,0 +1,126 @@
+<template>
+  <Teleport to="body">
+    <Transition :name="`drawer-slide-${placement}`"
+      @afterLeave="contentVisible = false"
+      @before-enter="contentVisible = true"
+    >
+      <div :class="['drawer', placement]" v-show="visible" :style="{ width: props.width + 'px' }">
+        <div class="header">
+          <slot name="title"></slot>
+          <span class="close-btn" @click="emit('update:visible', false)"><IconClose /></span>
+        </div>
+        <div class="content" v-if="contentVisible" :style="contentStyle">
+          <slot></slot>
+        </div>
+      </div>
+    </Transition>
+  </Teleport>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, type CSSProperties } from 'vue'
+
+const props = withDefaults(defineProps<{
+  visible: boolean
+  width?: number
+  contentStyle?: CSSProperties
+  placement?: 'left' | 'right'
+}>(), {
+  width: 320,
+  placement: 'right',
+})
+
+const emit = defineEmits<{
+  (event: 'update:visible', payload: boolean): void
+}>()
+
+const contentVisible = ref(false)
+
+const contentStyle = computed(() => {
+  return {
+    width: props.width + 'px',
+    ...(props.contentStyle || {})
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.drawer {
+  height: 100%;
+  position: fixed;
+  top: 0;
+  bottom: 0;
+  z-index: 5000;
+  background: #fff;
+  display: flex;
+  flex-direction: column;
+
+  &.left {
+    left: 0;
+    box-shadow: 3px 0 6px -4px rgba(0, 0, 0, 0.12), 9px 0 28px 8px rgba(0, 0, 0, 0.05);
+  }
+  &.right {
+    right: 0;
+    box-shadow: -3px 0 6px -4px rgba(0, 0, 0, 0.12), -9px 0 28px 8px rgba(0, 0, 0, 0.05);
+  }
+}
+
+.header {
+  height: 50px;
+  padding: 0 15px;
+  position: relative;
+  display: flex;
+  align-items: center;
+
+  .close-btn {
+    width: 20px;
+    height: 20px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    top: 15px;
+    right: 15px;
+    cursor: pointer;
+  }
+}
+.content {
+  padding: 0 15px;
+  overflow: auto;
+  flex: 1;
+}
+
+.drawer-slide-right-enter-active {
+  animation: drawer-slide-right-enter .25s both ease;
+}
+.drawer-slide-right-leave-active {
+  animation: drawer-slide-right-leave .25s both ease;
+}
+.drawer-slide-left-enter-active {
+  animation: drawer-slide-left-enter .25s both ease;
+}
+.drawer-slide-left-leave-active {
+  animation: drawer-slide-left-leave .25s both ease;
+}
+
+@keyframes drawer-slide-right-enter {
+  from {
+    transform: translateX(100%);
+  }
+}
+@keyframes drawer-slide-right-leave {
+  to {
+    transform: translateX(100%);
+  }
+}
+@keyframes drawer-slide-left-enter {
+  from {
+    transform: translateX(-100%);
+  }
+}
+@keyframes drawer-slide-left-leave {
+  to {
+    transform: translateX(-100%);
+  }
+}
+</style>

+ 45 - 0
src/components/FileInput.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="file-input" @click="handleClick()">
+    <slot></slot>
+    <input 
+      class="input"
+      type="file" 
+      name="upload" 
+      ref="inputRef" 
+      :accept="accept" 
+      @change="$event => handleChange($event)"
+    >
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+
+withDefaults(defineProps<{
+  accept?: string
+}>(), {
+  accept: 'image/*',
+})
+
+const emit = defineEmits<{
+  (event: 'change', payload: FileList): void
+}>()
+
+const inputRef = ref<HTMLInputElement>()
+
+const handleClick = () => {
+  if (!inputRef.value) return
+  inputRef.value.value = ''
+  inputRef.value.click()
+}
+const handleChange = (e: Event) => {
+  const files = (e.target as HTMLInputElement).files
+  if (files) emit('change', files)
+}
+</script>
+
+<style lang="scss" scoped>
+.input {
+  display: none;
+}
+</style>

+ 66 - 0
src/components/FullscreenSpin.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="fullscreen-spin" v-if="loading">
+    <div class="spin">
+      <div class="spinner"></div>
+      <div class="text">{{tip}}</div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+withDefaults(defineProps<{
+  loading?: boolean
+  tip?: string
+}>(), {
+  loading: false,
+  tip: '',
+})
+</script>
+
+<style lang="scss" scoped>
+.fullscreen-spin {
+  position: fixed;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  z-index: 100;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: rgba($color: #f1f1f1, $alpha: .7);
+}
+.spin {
+  width: 200px;
+  height: 200px;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  margin-top: -100px;
+  margin-left: -100px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+.spinner {
+  width: 36px;
+  height: 36px;
+  border: 3px solid $themeColor;
+  border-top-color: transparent;
+  border-radius: 50%;
+  animation: spinner .8s linear infinite;
+}
+.text {
+  margin-top: 20px;
+  color: $themeColor;
+}
+@keyframes spinner {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+</style>

+ 150 - 0
src/components/GradientBar.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="gradient-bar">
+    <div class="bar" ref="barRef" :style="{ backgroundImage: gradientStyle }" @click="$event => addPoint($event)"></div>
+    <div class="point" 
+      :class="{ 'active': activeIndex === index }"
+      v-for="(item, index) in points" 
+      :key="item.pos + '-' + index" 
+      :style="{
+        backgroundColor: item.color,
+        left: `calc(${item.pos}% - 5px)`,
+      }"
+      @mousedown.left="movePoint(index)"
+      @click.right="removePoint(index)"
+    ></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import type { GradientColor } from '@/types/slides'
+import { ref, computed, watchEffect, watch } from 'vue'
+
+const props = defineProps<{
+  value: GradientColor[]
+}>()
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: GradientColor[]): void
+  (event: 'update:index', payload: number): void
+}>()
+
+const points = ref<GradientColor[]>([])
+
+const barRef = ref<HTMLElement>()
+const activeIndex = ref(0)
+
+watchEffect(() => {
+  points.value = props.value
+  if (activeIndex.value > props.value.length - 1) activeIndex.value = 0
+})
+
+watch(activeIndex, () => {
+  emit('update:index', activeIndex.value)
+})
+
+const gradientStyle = computed(() => {
+  const list = points.value.map(item => `${item.color} ${item.pos}%`)
+  return `linear-gradient(to right, ${list.join(',')})`
+})
+
+const removePoint = (index: number) => {
+  if (props.value.length <= 2) return
+
+  if (index === activeIndex.value) {
+    activeIndex.value = (index - 1 < 0) ? 0 : index - 1
+  }
+  else if (activeIndex.value === props.value.length - 1) {
+    activeIndex.value = props.value.length - 2
+  }
+
+  const values = props.value.filter((item, _index) => _index !== index)
+  emit('update:value', values)
+}
+
+const movePoint = (index: number) => {
+  let isMouseDown = true
+
+  document.onmousemove = e => {
+    if (!isMouseDown) return
+    if (!barRef.value) return
+
+    let pos = Math.round((e.clientX - barRef.value.getBoundingClientRect().left) / barRef.value.clientWidth * 100)
+    if (pos > 100) pos = 100
+    if (pos < 0) pos = 0
+
+    points.value = points.value.map((item, _index) => {
+      if (_index === index) return { ...item, pos }
+      return item
+    })
+  }
+  document.onmouseup = () => {
+    isMouseDown = false
+
+    const point = points.value[index]
+    const _points = [...points.value]
+    _points.splice(index, 1)
+
+    let targetIndex = 0
+    for (let i = 0; i < _points.length; i++) {
+      if (point.pos > _points[i].pos) targetIndex = i + 1
+    }
+
+    activeIndex.value = targetIndex
+    _points.splice(targetIndex, 0, point)
+
+    emit('update:value', _points)
+    
+    document.onmousemove = null
+    document.onmouseup = null
+  }
+}
+
+const addPoint = (e: MouseEvent) => {
+  if (props.value.length >= 6) return
+  if (!barRef.value) return
+  const pos = Math.round((e.clientX - barRef.value.getBoundingClientRect().left) / barRef.value.clientWidth * 100)
+
+  let targetIndex = 0
+  for (let i = 0; i < props.value.length; i++) {
+    if (pos > props.value[i].pos) targetIndex = i + 1
+  }
+  const color = props.value[targetIndex - 1] ? props.value[targetIndex - 1].color : props.value[targetIndex].color
+  const values = [...props.value]
+  values.splice(targetIndex, 0, { pos, color })
+  activeIndex.value = targetIndex
+  emit('update:value', values)
+}
+</script>
+
+<style lang="scss" scoped>
+.gradient-bar {
+  width: calc(100% - 10px);
+  height: 18px;
+  padding: 1px 0;
+  margin: 3px 0;
+  position: relative;
+  left: 5px;
+
+  .bar {
+    height: 16px;
+    border: 1px solid #d9d9d9;
+  }
+  .point {
+    width: 10px;
+    height: 18px;
+    background-color: #fff;
+    position: absolute;
+    top: 0;
+    border: 2px solid #fff;
+    outline: 1px solid #d9d9d9;
+    box-shadow: 0 0 2px 2px #d9d9d9;
+    border-radius: 1px;
+    cursor: pointer;
+
+    &.active {
+      outline: 1px solid $themeColor;
+      box-shadow: 0 0 2px 2px $themeColor;
+    }
+  }
+}
+</style>

+ 132 - 0
src/components/Input.vue

@@ -0,0 +1,132 @@
+<template>
+  <div 
+    class="input"
+    :class="{
+      'disabled': disabled,
+      'focused': focused,
+      'simple': simple,
+    }"
+  >
+    <span class="prefix">
+      <slot name="prefix"></slot>
+    </span>
+    <input
+      type="text"
+      ref="inputRef"
+      :disabled="disabled"
+      :value="value" 
+      :placeholder="placeholder"
+      @input="$event => handleInput($event)"
+      @focus="$event => handleFocus($event)"
+      @blur="$event => handleBlur($event)"
+      @change="$event => emit('change', $event)"
+      @keydown.enter="$event => emit('enter', $event)"
+    />
+    <span class="suffix">
+      <slot name="suffix"></slot>
+    </span>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+
+withDefaults(defineProps<{
+  value: string
+  disabled?: boolean
+  placeholder?: string
+  simple?: boolean
+}>(), {
+  disabled: false,
+  placeholder: '',
+  simple: false,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: string): void
+  (event: 'input', payload: Event): void
+  (event: 'change', payload: Event): void
+  (event: 'blur', payload: Event): void
+  (event: 'focus', payload: Event): void
+  (event: 'enter', payload: Event): void
+}>()
+
+const focused = ref(false)
+
+const handleInput = (e: Event) => {
+  emit('update:value', (e.target as HTMLInputElement).value)
+}
+const handleBlur = (e: Event) => {
+  focused.value = false
+  emit('blur', e)
+}
+const handleFocus = (e: Event) => {
+  focused.value = true
+  emit('focus', e)
+}
+
+const inputRef = ref<HTMLInputElement>()
+const focus = () => {
+  if (inputRef.value) inputRef.value.focus()
+}
+
+defineExpose({
+  focus,
+})
+</script>
+
+<style lang="scss" scoped>
+.input {
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  padding: 0 5px;
+  border-radius: $borderRadius;
+  transition: border-color .25s;
+  font-size: 13px;
+  display: flex;
+
+  input {
+    min-width: 0;
+    height: 30px;
+    outline: 0;
+    border: 0;
+    line-height: 30px;
+    vertical-align: top;
+    color: $textColor;
+    padding: 0 5px;
+    flex: 1;
+    font-size: 13px;
+    font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji';
+
+    &::placeholder {
+      color: #bfbfbf;
+    }
+  }
+
+  &:not(.disabled):hover, &.focused {
+    border-color: $themeColor;
+  }
+
+  &.disabled {
+    background-color: #f5f5f5;
+    border-color: #dcdcdc;
+    color: #b7b7b7;
+
+    input {
+      color: #b7b7b7;
+    }
+  }
+
+  &.simple {
+    border: 0;
+  }
+
+  .prefix, .suffix {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    line-height: 30px;
+    user-select: none;
+  }
+}
+</style>

+ 57 - 0
src/components/LaTeXEditor/FormulaContent.vue

@@ -0,0 +1,57 @@
+<template>
+  <svg 
+    class="formula-content"
+    overflow="visible" 
+    :width="box.w + 32"
+    :height="box.h + 32"
+    stroke="#000" 
+    stroke-width="1" 
+    fill="none" 
+    stroke-linecap="round"
+    stroke-linejoin="round"
+  >
+    <g 
+      :transform="`scale(${scale}, ${scale}) translate(0,0) matrix(1,0,0,1,0,0)`"
+      transform-origin="0 50%"
+    >
+      <path :d="pathd"></path>
+    </g>
+  </svg>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, watch } from 'vue'
+import { hfmath } from './hfmath'
+
+const props = defineProps<{
+  latex: string
+  width: number
+  height: number
+}>()
+
+const box = ref({ x: 0, y: 0, w: 0, h: 0 })
+const pathd = ref('')
+
+watch(() => props.latex, () => {
+  const eq = new hfmath(props.latex)
+  pathd.value = eq.pathd({})
+  box.value = eq.box({})
+}, { immediate: true })
+
+const scale = computed(() => {
+  const boxW = box.value.w + 32
+  const boxH = box.value.h + 32
+
+  if (boxW > props.width || boxH > props.height) {
+    if (boxW / boxH > props.width / props.height) return props.width / boxW
+    return props.height / boxH
+  }
+  return 1
+})
+</script>
+
+<style lang="scss" scoped>
+svg {
+  overflow: hidden;
+}
+</style>

+ 20 - 0
src/components/LaTeXEditor/SymbolContent.vue

@@ -0,0 +1,20 @@
+<template>
+  <div class="symbol-content" v-html="svg"></div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import { hfmath } from './hfmath'
+
+const props = defineProps<{
+  latex: string
+}>()
+
+const svg = computed(() => {
+  const eq = new hfmath(props.latex)
+  return eq.svg({
+    SCALE_X: 10,
+    SCALE_Y: 10,
+  })
+})
+</script>

+ 5 - 0
src/components/LaTeXEditor/hfmath.ts

@@ -0,0 +1,5 @@
+import { hfmath, CONFIG as hfmathConfig } from 'hfmath'
+
+hfmathConfig.SUB_SUP_SCALE = 0.5
+
+export { hfmath }

+ 265 - 0
src/components/LaTeXEditor/index.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="latex-editor">
+    <div class="container">
+      <div class="left">
+        <div class="input-area">
+          <TextArea v-model:value="latex" placeholder="输入 LaTeX 公式" ref="textAreaRef" />
+        </div>
+        <div class="preview">
+          <div class="placeholder" v-if="!latex">公式预览</div>
+          <div class="preview-content" v-else>
+            <FormulaContent
+              :width="518"
+              :height="138"
+              :latex="latex"
+            />
+          </div>
+        </div>
+      </div>
+      <div class="right">
+        <Tabs 
+          :tabs="tabs" 
+          v-model:value="toolbarState" 
+          card
+        />
+        <div class="content">
+          <div class="symbol" v-if="toolbarState === 'symbol'">
+            <Tabs 
+              :tabs="symbolTabs" 
+              v-model:value="selectedSymbolKey" 
+              spaceBetween 
+              :tabsStyle="{ margin: '10px 10px 0' }" 
+            />
+            <div class="symbol-pool">
+              <div class="symbol-item" v-for="item in symbolPool" :key="item.latex" @click="insertSymbol(item.latex)">
+                <SymbolContent :latex="item.latex" />
+              </div>
+            </div>
+          </div>
+          <div class="formula" v-else>
+            <div class="formula-item" v-for="item in formulaList" :key="item.label">
+              <div class="formula-title">{{item.label}}</div>
+              <div class="formula-item-content" @click="latex = item.latex">
+                <FormulaContent
+                  :width="236"
+                  :height="60"
+                  :latex="item.latex"
+                />
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="footer">
+      <Button class="btn" @click="emit('close')">取消</Button>
+      <Button class="btn" type="primary" @click="update()">确定</Button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from 'vue'
+import { hfmath } from './hfmath'
+import { FORMULA_LIST, SYMBOL_LIST } from '@/configs/latex'
+import message from '@/utils/message'
+
+import FormulaContent from './FormulaContent.vue'
+import SymbolContent from './SymbolContent.vue'
+import Button from '../Button.vue'
+import TextArea from '../TextArea.vue'
+import Tabs from '../Tabs.vue'
+
+interface TabItem {
+  key: 'symbol' | 'formula'
+  label: string
+}
+
+const tabs: TabItem[] = [
+  { label: '常用符号', key: 'symbol' },
+  { label: '预置公式', key: 'formula' },
+]
+
+interface LatexResult {
+  latex: string
+  path: string
+  w: number
+  h: number
+}
+
+const props = withDefaults(defineProps<{
+  value?: string
+}>(), {
+  value: '',
+})
+
+const emit = defineEmits<{
+  (event: 'update', payload: LatexResult): void
+  (event: 'close'): void
+}>()
+
+const formulaList = FORMULA_LIST
+
+const symbolTabs = SYMBOL_LIST.map(item => ({
+  label: item.label,
+  key: item.type,
+}))
+
+const latex = ref('')
+const toolbarState = ref<'symbol' | 'formula'>('symbol')
+const textAreaRef = ref<InstanceType<typeof TextArea>>()
+
+const selectedSymbolKey = ref(SYMBOL_LIST[0].type)
+const symbolPool = computed(() => {
+  const selectedSymbol = SYMBOL_LIST.find(item => item.type === selectedSymbolKey.value)
+  return selectedSymbol?.children || []
+})
+
+onMounted(() => {
+  if (props.value) latex.value = props.value
+})
+
+const update = () => {
+  if (!latex.value) return message.error('公式不能为空')
+
+  const eq = new hfmath(latex.value)
+  const pathd = eq.pathd({})
+  const box = eq.box({})
+  
+  emit('update', {
+    latex: latex.value,
+    path: pathd,
+    w: box.w + 32,
+    h: box.h + 32,
+  })
+}
+
+const insertSymbol = (latex: string) => {
+  if (!textAreaRef.value) return
+  textAreaRef.value.focus()
+  document.execCommand('insertText', false, latex)
+}
+</script>
+
+<style lang="scss" scoped>
+.latex-editor {
+  height: 560px;
+}
+.container {
+  height: calc(100% - 50px);
+  display: flex;
+}
+.left {
+  width: 540px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  flex-shrink: 0;
+}
+.input-area {
+  flex: 1;
+
+  textarea {
+    height: 100% !important;
+    border-color: $borderColor !important;
+    padding: 10px !important;
+    font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace;
+
+    &:focus {
+      box-shadow: none !important;
+    }
+  }
+}
+.preview {
+  height: 160px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+  margin-top: 20px;
+  border: 1px solid $borderColor;
+  user-select: none;
+}
+.placeholder {
+  color: #888;
+  font-size: 13px;
+}
+.preview-content {
+  width: 100%;
+  height: 100%;
+  padding: 10px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.right {
+  width: 280px;
+  height: 100%;
+  margin-left: 20px;
+  border: solid 1px $borderColor;
+  background-color: #fff;
+  display: flex;
+  flex-direction: column;
+  user-select: none;
+}
+.content {
+  height: calc(100% - 40px);
+  font-size: 13px;
+}
+.formula {
+  height: 100%;
+  padding: 12px;
+
+  @include overflow-overlay();
+}
+.formula-item {
+  & + .formula-item {
+    margin-top: 10px;
+  }
+
+  .formula-title {
+    margin-bottom: 5px;
+  }
+  .formula-item-content {
+    height: 60px;
+    padding: 5px;
+    display: flex;
+    align-items: center;
+    background-color: $lightGray;
+    cursor: pointer;
+  }
+}
+.symbol {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+.symbol-pool {
+  display: flex;
+  flex-wrap: wrap;
+  flex: 1;
+  padding: 12px;
+
+  @include overflow-overlay();
+}
+.symbol-item {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  &:hover {
+    background-color: $lightGray;
+    cursor: pointer;
+  }
+}
+.footer {
+  height: 50px;
+  display: flex;
+  justify-content: flex-end;
+  align-items: flex-end;
+
+  .btn {
+    margin-left: 10px;
+  }
+}
+</style>

+ 182 - 0
src/components/Message.vue

@@ -0,0 +1,182 @@
+<template>
+  <Transition 
+    name="message-fade" 
+    appear 
+    mode="in-out"
+    @beforeLeave="emit('close')"
+    @afterLeave="emit('destroy')"
+  >
+    <div class="message" :id="id" v-if="visible">
+      <div class="message-container"
+        @mouseenter="clearTimer()"
+        @mouseleave="startTimer()"
+      >
+        <div class="icons">
+          <IconAttention theme="filled" size="18" fill="#faad14" v-if="type === 'warning'" />
+          <IconCheckOne theme="filled" size="18" fill="#52c41a" v-if="type === 'success'" />
+          <IconCloseOne theme="filled" size="18" fill="#ff4d4f" v-if="type === 'error'" />
+          <IconInfo theme="filled" size="18" fill="#1677ff" v-if="type === 'info'" />
+        </div>
+        <div class="content">
+          <div class="title" v-if="title">{{ title }}</div>
+          <div class="description">{{ message }}</div>
+        </div>
+        <div class="control" v-if="closable">
+          <span 
+            class="close-btn"
+            @click="close()"
+          >
+            <IconCloseSmall />
+          </span>
+        </div>
+      </div>
+    </div>
+  </Transition>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, ref, onBeforeMount } from 'vue'
+import { icons } from '@/plugins/icon'
+
+const {
+  IconAttention,
+  IconCheckOne,
+  IconCloseOne,
+  IconInfo,
+  IconCloseSmall,
+} = icons
+
+const props = withDefaults(defineProps<{
+  id: string
+  message: string
+  type?: string
+  title?: string
+  duration?: number
+  closable?: boolean
+}>(), {
+  type: 'success',
+  title: '',
+  duration: 3000,
+  closable: false,
+})
+
+const emit = defineEmits<{
+  (event: 'close'): void
+  (event: 'destroy'): void
+}>()
+
+const visible = ref(true)
+const timer = ref<number | null>(null)
+
+const startTimer = () => {
+  if (props.duration <= 0) return
+  timer.value = setTimeout(close, props.duration)
+}
+const clearTimer = () => {
+  if (timer.value) clearTimeout(timer.value)
+}
+
+const close = () => visible.value = false
+
+onBeforeMount(() => {
+  clearTimer()
+})
+onMounted(() => {
+  startTimer()
+})
+
+defineExpose({
+  close,
+})
+</script>
+
+<style lang="scss" scoped>
+.message {
+  max-width: 600px;
+
+  & + & {
+    margin-top: 15px;
+  }
+}
+.message-container {
+  min-width: 50px;
+  display: flex;
+  align-items: center;
+  padding: 10px;
+  font-size: 13px;
+  overflow: hidden;
+  border-radius: $borderRadius;
+  box-shadow: 0 1px 8px rgba(0, 0, 0, .15);
+  background: #fff;
+  pointer-events: all;
+  position: relative;
+
+  .icons {
+    display: flex;
+    align-items: center;
+    margin-right: 10px;
+  }
+  .title {
+    font-size: 14px;
+    font-weight: 700;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .content {
+    width: 100%;
+  }
+  .description {
+    line-height: 1.5;
+    color: $textColor;
+  }
+  .title + .description {
+    margin-top: 5px;
+  }
+  .control {
+    position: relative;
+    height: 100%;
+    margin-left: 10px;
+  }
+  .close-btn {
+    font-size: 15px;
+    color: #666;
+    display: flex;
+    align-items: center;
+    cursor: pointer;
+
+    &:hover {
+      color: $themeColor;
+    }
+  }
+}
+
+.message-fade-enter-active {
+  animation: message-fade-in-down .3s;
+}
+.message-fade-leave-active {
+  animation: message-fade-out .3s;
+}
+
+@keyframes message-fade-in-down {
+  0% {
+    opacity: 0;
+    transform: translateY(-20px);
+  }
+  100% {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+@keyframes message-fade-out {
+  0% {
+    opacity: 1;
+    margin-top: 0;
+  }
+  100% {
+    opacity: 0;
+    margin-top: -45px;
+  }
+}
+</style>

+ 154 - 0
src/components/Modal.vue

@@ -0,0 +1,154 @@
+<template>
+  <Teleport to="body">
+    <Transition name="modal-fade">
+      <div class="modal" ref="modalRef" v-show="visible" tabindex="-1" @keyup.esc="onEsc()">
+        <div class="mask" @click="onClickMask()"></div>
+        <Transition name="modal-zoom"
+          @afterLeave="contentVisible = false"
+          @before-enter="contentVisible = true"
+        >
+          <div class="modal-content" v-show="visible" :style="contentStyle">
+            <span class="close-btn" v-if="closeButton" @click="close()"><IconClose /></span>
+            <slot v-if="contentVisible"></slot>
+          </div>
+        </Transition>
+      </div>
+    </Transition>
+  </Teleport>
+</template>
+
+<script lang="ts" setup>
+import { computed, nextTick, ref, watch, type CSSProperties } from 'vue'
+import { icons } from '@/plugins/icon'
+
+const { IconClose } = icons
+
+const props = withDefaults(defineProps<{
+  visible: boolean
+  width?: number
+  closeButton?: boolean
+  closeOnClickMask?: boolean
+  closeOnEsc?: boolean
+  contentStyle?: CSSProperties
+}>(), {
+  width: 480,
+  closeButton: false,
+  closeOnClickMask: true,
+  closeOnEsc: true,
+})
+
+const modalRef = ref<HTMLDivElement>()
+
+const emit = defineEmits<{
+  (event: 'update:visible', payload: boolean): void
+  (event: 'closed'): void
+}>()
+
+const contentVisible = ref(false)
+
+const contentStyle = computed(() => {
+  return {
+    width: props.width + 'px',
+    ...(props.contentStyle || {})
+  }
+})
+
+watch(() => props.visible, () => {
+  if (props.visible) {
+    nextTick(() => modalRef.value!.focus())
+  }
+})
+
+const close = () => {
+  emit('update:visible', false)
+  emit('closed')
+}
+
+const onEsc = () => {
+  if (props.visible && props.closeOnEsc) close()
+}
+
+const onClickMask = () => {
+  if (props.closeOnClickMask) close()
+}
+</script>
+
+<style lang="scss" scoped>
+.modal, .mask {
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 5000;
+}
+
+.modal {
+  position: fixed;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  outline: 0;
+  border: 0;
+}
+
+.mask {
+  position: absolute;
+  background: rgba(0, 0, 0, .25);
+}
+
+.modal-content {
+  z-index: 5001;
+  padding: 20px;
+  background: #fff;
+  border-radius: $borderRadius;
+  overflow: hidden;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
+  position: relative;
+}
+
+.close-btn {
+  width: 20px;
+  height: 20px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  position: absolute;
+  top: 16px;
+  right: 16px;
+  cursor: pointer;
+}
+
+.modal-fade-enter-active {
+  animation: modal-fade-enter .25s both ease-in;
+}
+.modal-fade-leave-active {
+  animation: modal-fade-leave .25s both ease-out;
+}
+.modal-zoom-enter-active {
+  animation: modal-zoom-enter .25s both cubic-bezier(.4, 0, 0, 1.5);
+}
+.modal-zoom-leave-active {
+  animation: modal-zoom-leave .25s both;
+}
+
+@keyframes modal-fade-enter {
+  from {
+    opacity: 0;
+  }
+}
+@keyframes modal-fade-leave {
+  to {
+    opacity: 0;
+  }
+}
+@keyframes modal-zoom-enter {
+  from {
+    transform: scale3d(.3, .3, .3);
+  }
+}
+@keyframes modal-zoom-leave {
+  to {
+    transform: scale3d(.3, .3, .3);
+  }
+}
+</style>

+ 220 - 0
src/components/MoveablePanel.vue

@@ -0,0 +1,220 @@
+<template>
+  <div 
+    class="moveable-panel"
+    ref="moveablePanelRef"
+    :style="{
+      width: w + 'px',
+      height: h ? h + 'px' : 'auto',
+      left: x + 'px',
+      top: y + 'px',
+    }"
+  >
+    <template v-if="title">
+      <div class="header" @mousedown="$event => startMove($event)">
+        <div class="title">{{title}}</div>
+        <div class="close-btn" @click="emit('close')"><IconClose /></div>
+      </div>
+
+      <div class="content">
+        <slot></slot>
+      </div>
+    </template>
+
+    <div v-else class="content" @mousedown="$event => startMove($event)">
+      <slot></slot>
+    </div>
+
+    <div class="resizer" v-if="resizeable" @mousedown="$event => startResize($event)"></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from 'vue'
+
+const props = withDefaults(defineProps<{
+  width: number
+  height: number
+  minWidth?: number
+  minHeight?: number
+  maxWidth?: number
+  maxHeight?: number
+  left?: number
+  top?: number
+  title?: string
+  moveable?: boolean
+  resizeable?: boolean
+}>(), {
+  minWidth: 20,
+  minHeight: 20,
+  maxWidth: 500,
+  maxHeight: 500,
+  left: 10,
+  top: 10,
+  title: '',
+  moveable: true,
+  resizeable: false,
+})
+
+const emit = defineEmits<{
+  (event: 'close'): void
+}>()
+
+const x = ref(0)
+const y = ref(0)
+const w = ref(0)
+const h = ref(0)
+const moveablePanelRef = ref<HTMLElement>()
+const realHeight = computed(() => {
+  if (!h.value) {
+    return moveablePanelRef.value?.clientHeight || 0
+  }
+  return h.value
+})
+
+onMounted(() => {
+  if (props.left >= 0) x.value = props.left
+  else x.value = document.body.clientWidth + props.left - props.width
+
+  if (props.top >= 0) y.value = props.top
+  else y.value = document.body.clientHeight + props.top - realHeight.value
+
+  w.value = props.width
+  h.value = props.height
+})
+
+const startMove = (e: MouseEvent) => {
+  if (!props.moveable) return
+
+  let isMouseDown = true
+
+  const windowWidth = document.body.clientWidth
+  const clientHeight = document.body.clientHeight
+
+  const startPageX = e.pageX
+  const startPageY = e.pageY
+
+  const originLeft = x.value
+  const originTop = y.value
+
+  document.onmousemove = e => {
+    if (!isMouseDown) return
+
+    const moveX = e.pageX - startPageX
+    const moveY = e.pageY - startPageY
+
+    let left = originLeft + moveX
+    let top = originTop + moveY
+
+    if (left < 0) left = 0
+    if (top < 0) top = 0
+    if (left + w.value > windowWidth) left = windowWidth - w.value
+    if (top + realHeight.value > clientHeight) top = clientHeight - realHeight.value
+
+    x.value = left
+    y.value = top
+  }
+  document.onmouseup = () => {
+    isMouseDown = false
+
+    document.onmousemove = null
+    document.onmouseup = null
+  }
+}
+
+const startResize = (e: MouseEvent) => {
+  if (!props.resizeable) return
+
+  let isMouseDown = true
+
+  const startPageX = e.pageX
+  const startPageY = e.pageY
+
+  const originWidth = w.value
+  const originHeight = h.value
+
+  document.onmousemove = e => {
+    if (!isMouseDown) return
+
+    const moveX = e.pageX - startPageX
+    const moveY = e.pageY - startPageY
+
+    let width = originWidth + moveX
+    let height = originHeight + moveY
+
+    if (width < props.minWidth) width = props.minWidth
+    if (height < props.minHeight) height = props.minHeight
+    if (width > props.maxWidth) width = props.maxWidth
+    if (height > props.maxHeight) height = props.maxHeight
+
+    w.value = width
+    h.value = height
+  }
+  document.onmouseup = () => {
+    isMouseDown = false
+
+    document.onmousemove = null
+    document.onmouseup = null
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.moveable-panel {
+  position: fixed;
+  background-color: #fff;
+  box-shadow: $boxShadow;
+  border: 1px solid $borderColor;
+  border-radius: $borderRadius;
+  display: flex;
+  flex-direction: column;
+  z-index: 999;
+}
+.resizer {
+  width: 10px;
+  height: 10px;
+  position: absolute;
+  bottom: 0;
+  right: 0;
+  cursor: se-resize;
+
+  &::after {
+    content: "";
+    position: absolute;
+    bottom: -4px;
+    right: -4px;
+    transform: rotate(45deg);
+    transform-origin: center;
+    width: 0;
+    height: 0;
+    border: 6px solid transparent;
+    border-left-color: #e1e1e1;
+  }
+}
+.header {
+  height: 40px;
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid #f0f0f0;
+  cursor: move;
+}
+.title {
+  flex: 1;
+  font-size: 13px;
+  padding-left: 10px;
+}
+.close-btn {
+  width: 40px;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #666;
+  font-size: 13px;
+  cursor: pointer;
+}
+.content {
+  flex: 1;
+  padding: 10px;
+  overflow: auto;
+}
+</style>

+ 201 - 0
src/components/NumberInput.vue

@@ -0,0 +1,201 @@
+<template>
+  <div 
+    class="number-input"
+    :class="{
+      'disabled': disabled,
+      'focused': focused,
+    }"
+  >
+    <span class="prefix">
+      <slot name="prefix"></slot>
+    </span>
+    <div class="input-wrap">
+      <input
+        type="text"
+        :disabled="disabled"
+        v-model="number" 
+        :placeholder="placeholder"
+        @input="$event => emit('input', $event)"
+        @focus="$event => handleFocus($event)"
+        @blur="$event => handleBlur($event)"
+        @change="$event => emit('change', $event)"
+        @keydown.enter="$event => handleEnter($event)"
+      />
+      <div class="handlers">
+        <span class="handler" @click="number += step">
+          <svg fill="currentColor" width="1em" height="1em" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg>
+        </span>
+        <span class="handler" @click="number -= step">
+          <svg fill="currentColor" width="1em" height="1em" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg>
+        </span>
+      </div>
+    </div>
+    <span class="suffix">
+      <slot name="suffix"></slot>
+    </span>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+
+const props = withDefaults(defineProps<{
+  value: number
+  disabled?: boolean
+  placeholder?: string
+  min?: number
+  max?: number
+  step?: number
+}>(), {
+  disabled: false,
+  placeholder: '',
+  min: 0,
+  max: Infinity,
+  step: 1,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: number): void
+  (event: 'input', payload: Event): void
+  (event: 'change', payload: Event): void
+  (event: 'blur', payload: Event): void
+  (event: 'focus', payload: Event): void
+  (event: 'enter', payload: Event): void
+}>()
+
+const number = ref(0)
+const focused = ref(false)
+
+watch(() => props.value, () => {
+  if (props.value !== number.value) {
+    number.value = props.value
+  }
+}, {
+  immediate: true,
+})
+
+watch(number, () => {
+  const value = +number.value
+  if (isNaN(value)) return
+  else if (value > props.max) return
+  else if (value < props.min) return
+
+  number.value = value
+  emit('update:value', number.value)
+})
+
+const checkAndEmitValue = () => {
+  let value = +number.value
+  if (isNaN(value)) value = props.min
+  else if (value > props.max) value = props.max
+  else if (value < props.min) value = props.min
+
+  number.value = value
+  emit('update:value', number.value)
+}
+
+const handleEnter = (e: Event) => {
+  checkAndEmitValue()
+  emit('enter', e)
+}
+
+const handleBlur = (e: Event) => {
+  checkAndEmitValue()
+  focused.value = false
+  emit('blur', e)
+}
+const handleFocus = (e: Event) => {
+  focused.value = true
+  emit('focus', e)
+}
+</script>
+
+<style lang="scss" scoped>
+.number-input {
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  padding: 0 0 0 5px;
+  border-radius: $borderRadius;
+  transition: border-color .25s;
+  font-size: 13px;
+  display: inline-flex;
+
+  .input-wrap {
+    flex: 1;
+    color: $textColor;
+    padding: 0 0 0 5px;
+    position: relative;
+  }
+  &:not(.disabled) .input-wrap:hover .handlers {
+    opacity: 1;
+  }
+  .handlers {
+    width: 20px;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    right: 0;
+    display: flex;
+    flex-direction: column;
+    font-size: 6px;
+    color: #999;
+    opacity: 0;
+    user-select: none;
+    transition: opacity .25s;
+
+    .handler {
+      width: 100%;
+      height: 50%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      border-left: 1px solid #d9d9d9;
+      cursor: pointer;
+
+      & + .handler {
+        border-top: 1px solid #d9d9d9;
+      }
+
+      &:hover {
+        color: $themeColor;
+      }
+    }
+  }
+  input {
+    width: 100%;
+    min-width: 0;
+    padding: 0;
+    height: 30px;
+    line-height: 30px;
+    outline: 0;
+    border: 0;
+    font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji';
+
+    &::placeholder {
+      color: #bfbfbf;
+    }
+  }
+
+  &:not(.disabled):hover, &.focused {
+    border-color: $themeColor;
+  }
+
+  &.disabled {
+    background-color: #f5f5f5;
+    border-color: #dcdcdc;
+    color: #b7b7b7;
+
+    input {
+      color: #b7b7b7;
+    }
+  }
+
+  .prefix, .suffix {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    line-height: 30px;
+    user-select: none;
+  }
+}
+</style>

+ 103 - 0
src/components/Popover.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="popover" :class="{ 'center': center }" ref="triggerRef">
+    <div class="popover-content" :style="contentStyle" ref="contentRef">
+      <slot name="content" v-if="contentVisible"></slot>
+    </div>
+    <slot></slot>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { type CSSProperties, onMounted, onUnmounted, ref, watch, computed } from 'vue'
+import tippy, { type Instance, type Placement } from 'tippy.js'
+
+import 'tippy.js/animations/scale.css'
+
+const props = withDefaults(defineProps<{
+  value?: boolean
+  trigger?: 'click' | 'mouseenter' | 'manual'
+  placement?: Placement
+  appendTo?: HTMLElement | 'parent'
+  contentStyle?: CSSProperties
+  center?: boolean
+  offset?: number
+}>(), {
+  value: false,
+  trigger: 'click',
+  placement: 'bottom',
+  center: false,
+  offset: 8,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: boolean): void
+}>()
+
+const instance = ref<Instance>()
+const triggerRef = ref<HTMLElement>()
+const contentRef = ref<HTMLElement>()
+const contentVisible = ref(false)
+
+const contentStyle = computed(() => {
+  return props.contentStyle || {}
+})
+
+watch(() => props.value, () => {
+  if (!instance.value) return
+  if (props.value) instance.value.show()
+  else instance.value.hide()
+})
+
+onUnmounted(() => {
+  if (instance.value) instance.value.destroy()
+})
+
+onMounted(() => {
+  instance.value = tippy(triggerRef.value!, {
+    content: contentRef.value!,
+    allowHTML: true,
+    trigger: props.trigger,
+    placement: props.placement,
+    interactive: true,
+    appendTo: props.appendTo || document.body,
+    maxWidth: 'none',
+    offset: [0, props.offset],
+    duration: 200,
+    animation: 'scale',
+    theme: 'popover',
+    onShow() {
+      contentVisible.value = true
+    },
+    onShown() {
+      if (!props.value) emit('update:value', true)
+    },
+    onHidden() {
+      if (props.value) emit('update:value', false)
+      contentVisible.value = false
+    },
+  })
+})
+</script>
+
+<style lang="scss" scoped>
+.popover.center {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.popover-content {
+  background-color: #fff;
+  padding: 10px;
+  border: 1px solid $borderColor;
+  box-shadow: $boxShadow;
+  border-radius: $borderRadius;
+  font-size: 13px;
+}
+</style>
+
+<style lang="scss">
+.tippy-box[data-theme~='popover'] {
+  border: 0;
+  outline: 0;
+}
+</style>

+ 38 - 0
src/components/PopoverMenuItem.vue

@@ -0,0 +1,38 @@
+<template>
+  <div class="popover-menu-item" :class="{ 'center': center }" @click="emit('click')">
+    <slot></slot>
+  </div>
+</template>
+
+<script lang="ts" setup>
+withDefaults(defineProps<{
+  center?: boolean
+}>(), {
+  center: false,
+})
+
+const emit = defineEmits<{
+  (event: 'click'): void
+}>()
+</script>
+
+<style lang="scss" scoped>
+.popover-menu-item {
+  min-width: 80px;
+  padding: 6px 10px;
+  border-radius: $borderRadius;
+  font-size: 13px;
+  cursor: pointer;
+
+  &.center {
+    text-align: center;
+  }
+
+  &:hover {
+    background-color: #f1f1f1;
+  }
+  & + .popover-menu-item {
+    margin-top: 2px;
+  }
+}
+</style>

+ 26 - 0
src/components/RadioButton.vue

@@ -0,0 +1,26 @@
+<template>
+  <Button 
+    :checked="!disabled && _value === value"
+    :disabled="disabled"
+    type="radio"
+    @click="!disabled && updateValue(value)"
+  >
+    <slot></slot>
+  </Button>
+</template>
+
+<script lang="ts" setup>
+import { inject } from 'vue'
+import { injectKeyRadioGroupValue, type RadioGroupValue } from '@/types/injectKey'
+
+import Button from './Button.vue'
+
+const { value: _value, updateValue } = inject(injectKeyRadioGroupValue) as RadioGroupValue
+
+withDefaults(defineProps<{
+  value: string
+  disabled?: boolean
+}>(), {
+  disabled: false,
+})
+</script>

+ 35 - 0
src/components/RadioGroup.vue

@@ -0,0 +1,35 @@
+<template>
+  <ButtonGroup class="radio-group">
+    <slot></slot>
+  </ButtonGroup>
+</template>
+
+<script lang="ts" setup>
+import { computed, provide } from 'vue'
+import { injectKeyRadioGroupValue } from '@/types/injectKey'
+
+import ButtonGroup from './ButtonGroup.vue'
+
+const props = withDefaults(defineProps<{
+  value: string
+  disabled?: boolean
+}>(), {
+  disabled: false,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: string): void
+}>()
+
+const updateValue = (value: string) => {
+  if (props.disabled) return
+  emit('update:value', value)
+}
+
+const value = computed(() => props.value)
+
+provide(injectKeyRadioGroupValue, {
+  value,
+  updateValue,
+})
+</script>

+ 204 - 0
src/components/Select.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="select-wrap" v-if="disabled">
+    <div class="select disabled" ref="selectRef">
+      <div class="selector">{{ value }}</div>
+      <div class="icon">
+        <slot name="icon">
+          <IconDown :size="14" />
+        </slot>
+      </div>
+    </div>
+  </div>
+  <Popover 
+    class="select-wrap"
+    trigger="click" 
+    v-model:value="popoverVisible" 
+    placement="bottom"
+    :contentStyle="{
+      padding: 0,
+      boxShadow: '0 6px 16px 0 rgba(0, 0, 0, 0.08)',
+    }"
+    v-else
+  >
+    <template #content>
+      <template v-if="search">
+        <Input ref="searchInputRef" simple :placeholder="searchLabel" v-model:value="searchKey" :style="{ width: width + 2 + 'px' }" />
+        <Divider :margin="0" />
+      </template>
+      <div class="options" :style="{ width: width + 2 + 'px' }">
+        <div class="option" 
+          :class="{
+            'disabled': option.disabled,
+            'selected': option.value === value,
+          }"
+          v-for="option in showOptions" 
+          :key="option.value"
+          @click="handleSelect(option)"
+        >{{ option.label }}</div>
+      </div>
+    </template>
+    <div class="select" ref="selectRef">
+      <div class="selector">{{ showLabel }}</div>
+      <div class="icon">
+        <slot name="icon">
+          <IconDown :size="14" />
+        </slot>
+      </div>
+    </div>
+  </Popover>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, onUnmounted, ref, watch, nextTick, onBeforeUnmount } from 'vue'
+import Popover from './Popover.vue'
+import Input from './Input.vue'
+import Divider from './Divider.vue'
+
+interface SelectOption {
+  label: string
+  value: string | number
+  disabled?: boolean
+}
+
+const props = withDefaults(defineProps<{
+  value: string | number
+  options: SelectOption[]
+  disabled?: boolean
+  search?: boolean
+  searchLabel?: string
+}>(), {
+  disabled: false,
+  search: false,
+  searchLabel: '搜索',
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: string | number): void
+}>()
+
+const popoverVisible = ref(false)
+const selectRef = ref<HTMLElement>()
+const searchInputRef = ref<InstanceType<typeof Input>>()
+const width = ref(0)
+const searchKey = ref('')
+
+const showLabel = computed(() => {
+  return props.options.find(item => item.value === props.value)?.label || props.value
+})
+
+const showOptions = computed(() => {
+  if (!props.search) return props.options
+  if (!searchKey.value.trim()) return props.options
+  const opts = props.options.filter(item => {
+    return item.label.toLowerCase().indexOf(searchKey.value.toLowerCase()) !== -1
+  })
+  return opts.length ? opts : props.options
+})
+
+watch(popoverVisible, () => {
+  if (popoverVisible.value) {
+    nextTick(() => {
+      if (searchInputRef.value) searchInputRef.value.focus()
+    })
+  }
+  else searchKey.value = ''
+})
+onBeforeUnmount(() => {
+  searchKey.value = ''
+})
+
+const updateWidth = () => {
+  if (!selectRef.value) return
+  width.value = selectRef.value.clientWidth
+}
+const resizeObserver = new ResizeObserver(updateWidth)
+onMounted(() => {
+  if (!selectRef.value) return
+  resizeObserver.observe(selectRef.value)
+})
+onUnmounted(() => {
+  if (!selectRef.value) return
+  resizeObserver.unobserve(selectRef.value)
+})
+
+const handleSelect = (option: SelectOption) => {
+  if (option.disabled) return
+
+  emit('update:value', option.value)
+  popoverVisible.value = false
+}
+</script>
+
+<style lang="scss" scoped>
+.select {
+  width: 100%;
+  height: 32px;
+  padding-right: 32px;
+  border-radius: $borderRadius;
+  transition: border-color .25s;
+  font-size: 13px;
+  user-select: none;
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  position: relative;
+  cursor: pointer;
+
+  &:not(.disabled):hover {
+    border-color: $themeColor;
+  }
+
+  &.disabled {
+    background-color: #f5f5f5;
+    border-color: #dcdcdc;
+    color: #b7b7b7;
+    cursor: default;
+  }
+
+  .selector {
+    min-width: 50px;
+    height: 30px;
+    line-height: 30px;
+    padding-left: 10px;
+    @include ellipsis-oneline();
+  }
+}
+.options {
+  max-height: 260px;
+  padding: 5px;
+  overflow: auto;
+  text-align: left;
+  font-size: 13px;
+  user-select: none;
+}
+.option {
+  height: 32px;
+  line-height: 32px;
+  padding: 0 5px;
+  border-radius: $borderRadius;
+  @include ellipsis-oneline();
+
+  &.disabled {
+    color: #b7b7b7;
+  }
+  &:not(.disabled, .selected):hover {
+    background-color: rgba($color: $themeColor, $alpha: .05);
+    cursor: pointer;
+  }
+
+  &.selected {
+    color: $themeColor;
+    font-weight: 700;
+  }
+}
+.icon {
+  width: 32px;
+  height: 30px;
+  color: #bfbfbf;
+  position: absolute;
+  top: 0;
+  right: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+</style>

+ 54 - 0
src/components/SelectGroup.vue

@@ -0,0 +1,54 @@
+<template>
+  <div class="select-group">
+    <slot></slot>
+  </div>
+</template>
+
+<script lang="ts" setup>
+
+</script>
+
+<style lang="scss" scoped>
+.select-group {
+  display: flex;
+  align-items: center;
+
+  ::v-deep(.select-wrap) {
+    .select {
+      border-radius: 0;
+      border-left-width: 0;
+      border-right-width: 0;
+    }
+
+    & + .select-wrap {
+      .select {
+        border-left-width: 1px;
+      }
+    }
+
+    &:hover {
+      & + .select-wrap {
+        .select {
+          border-left-color: $themeColor;
+        }
+      }
+    }
+
+    &:first-child {
+      .select {
+        border-top-left-radius: $borderRadius;
+        border-bottom-left-radius: $borderRadius;
+        border-left-width: 1px;
+      }
+    }
+
+    &:last-child {
+      .select {
+        border-top-right-radius: $borderRadius;
+        border-bottom-right-radius: $borderRadius;
+        border-right-width: 1px;
+      }
+    }
+  }
+}
+</style>

+ 280 - 0
src/components/Slider.vue

@@ -0,0 +1,280 @@
+<template>
+  <div class="slider" :class="{ 'disabled': disabled }" ref="sliderRef" @mousedown="$event => handleMousedown($event)">
+    <div class="bar">
+      <template v-if="!range">
+        <div class="track" :style="{ width: `${percentage}%` }"></div>
+        <div class="thumb" :style="{ left: `${percentage}%` }" :data-tooltip="tooltipValue"></div>
+      </template>
+      <template v-else>
+        <div class="track" :style="{ width: `${end - start}%`, left: `${start}%` }"></div>
+        <div class="thumb" :style="{ left: `${start}%` }" :data-tooltip="tooltipRangeStartValue"></div>
+        <div class="thumb" :style="{ left: `${end}%` }" :data-tooltip="tooltipRangeEndValue"></div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, watch } from 'vue'
+import NP from 'number-precision'
+
+const getBoundingClientRectViewLeft = (element: HTMLElement) => {
+  return element.getBoundingClientRect().left
+}
+
+const props = withDefaults(defineProps<{
+  value: number | [number, number]
+  disabled?: boolean
+  min?: number
+  max?: number
+  step?: number
+  range?: boolean
+}>(), {
+  disabled: false,
+  min: 0,
+  max: 100,
+  step: 1,
+  range: false,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: number | [number, number]): void
+}>()
+
+const sliderRef = ref<HTMLElement>()
+const percentage = ref(0)
+const start = ref(0)
+const end = ref(0)
+const handler = ref<'start' | 'end'>('end')
+
+const getNewValue = (percentage: number) => {
+  let diff = percentage / 100 * (props.max - props.min)
+  if (props.step >= 1) diff = Math.fround(diff)
+  else {
+    const str = props.step.toString()
+    const match = str.match(/^[0.]*([1-9])/)
+
+    if (match) {
+      const targetNumber = match[1]
+      const position = str.indexOf(targetNumber) - 1
+      if (position > 0) {
+        const accuracy = Math.pow(10, position)
+        diff = Math.fround(diff * accuracy) / accuracy
+      }
+    }
+  }
+  return NP.plus(diff, props.min)
+}
+
+const tooltipValue = computed(() => {
+  return getNewValue(percentage.value)
+})
+const tooltipRangeStartValue = computed(() => {
+  return getNewValue(start.value)
+})
+const tooltipRangeEndValue = computed(() => {
+  return getNewValue(end.value)
+})
+
+watch(() => props.value, () => {
+  if (props.max === props.min) return
+  if (typeof props.value === 'number') {
+    percentage.value = (props.value - props.min) / (props.max - props.min) * 100
+  }
+  else {
+    start.value = (props.value[0] - props.min) / (props.max - props.min) * 100
+    end.value = (props.value[1] - props.min) / (props.max - props.min) * 100
+  }
+}, {
+  immediate: true,
+})
+
+const getPercentage = (e: MouseEvent | TouchEvent) => {
+  if (!sliderRef.value) return 0
+  const clientX = 'clientX' in e ? e.clientX : e.changedTouches[0].clientX
+  let progress = (clientX - getBoundingClientRectViewLeft(sliderRef.value)) / sliderRef.value.clientWidth
+  progress = Math.max(progress, 0)
+  progress = Math.min(progress, 1)
+
+  let _percentage = progress * 100
+  const step = props.step / (props.max - props.min) * 100
+  const remainder = _percentage % step
+
+  if (remainder > 0) {
+    if (remainder <= step / 2) _percentage = _percentage - remainder
+    else _percentage = _percentage - remainder + step
+  }
+  return _percentage
+}
+
+// 双滑块(范围)模式
+const updateRange = (e: MouseEvent | TouchEvent) => {
+  const value = getPercentage(e)
+
+  if (handler.value === 'start') start.value = value
+  else end.value = value
+}
+
+const updateRangeEnd = (e: MouseEvent | TouchEvent) => {
+  updatePercentage(e)
+  const newValue = getNewValue(percentage.value)
+  const oldValueArr = props.value as [number, number]
+  const newValueArr: [number, number] = handler.value === 'start' ? [newValue, oldValueArr[1]] : [oldValueArr[0], newValue]
+  if (newValueArr[0] > newValueArr[1]) {
+    [newValueArr[0], newValueArr[1]] = [newValueArr[1], newValueArr[0]]
+  }
+
+  emit('update:value', newValueArr)
+
+  document.removeEventListener('mousemove', updateRange)
+  document.removeEventListener('touchmove', updateRange)
+  document.removeEventListener('mouseup', updateRangeEnd)
+  document.removeEventListener('touchend', updateRangeEnd)
+}
+
+// 单滑块模式
+const updatePercentage = (e: MouseEvent | TouchEvent) => {
+  percentage.value = getPercentage(e)
+}
+
+const updatePercentageEnd = (e: MouseEvent | TouchEvent) => {
+  updatePercentage(e)
+  const newValue = getNewValue(percentage.value)
+
+  emit('update:value', newValue)
+
+  document.removeEventListener('mousemove', updatePercentage)
+  document.removeEventListener('touchmove', updatePercentage)
+  document.removeEventListener('mouseup', updatePercentageEnd)
+  document.removeEventListener('touchend', updatePercentageEnd)
+}
+
+const handleMousedown = (e: MouseEvent | TouchEvent) => {
+  if (props.disabled) return
+
+  if (props.range) {
+    const _percentage = getPercentage(e)
+    
+    if (Math.abs(_percentage - start.value) < Math.abs(_percentage - end.value)) {
+      handler.value = 'start'
+    }
+    else handler.value = 'end'
+
+    document.addEventListener('mousemove', updateRange)
+    document.addEventListener('touchmove', updateRange)
+    document.addEventListener('mouseup', updateRangeEnd)
+    document.addEventListener('touchend', updateRangeEnd)
+  }
+  else {
+    document.addEventListener('mousemove', updatePercentage)
+    document.addEventListener('touchmove', updatePercentage)
+    document.addEventListener('mouseup', updatePercentageEnd)
+    document.addEventListener('touchend', updatePercentageEnd)
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.slider {
+  width: 100%;
+  height: 12px;
+  padding: 4px 0;
+  user-select: none;
+
+  &.disabled {
+    .track {
+      background-color: #b4b4b4;
+    }
+
+    .thumb {
+      outline: 2px solid #b4b4b4;
+    }
+  }
+}
+.slider:not(.disabled) {
+  cursor: pointer;
+
+  .bar {
+    &:hover {
+      background-color: #f0f0f0;
+    }
+  }
+
+  .track {
+    &:hover {
+      background-color: $themeHoverColor;
+    }
+  }
+
+  .thumb {
+    &:hover, &:active {
+      outline: 4px solid $themeColor;
+    }
+  }
+}
+
+.bar {
+  width: calc(100% - 10px);
+  margin-left: 5px;
+  height: 4px;
+  border-radius: 2px;
+  position: relative;
+  background-color: #f5f5f5;
+  user-select: none;
+  transition: background-color .2s;
+}
+
+.track {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  background-color: $themeColor;
+  transition: background-color .2s;
+}
+
+.thumb {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  width: 10px;
+  height: 10px;
+  background-color: #fff;
+  outline: 2px solid $themeColor;
+  transform: translate(-50%, -50%);
+  border-radius: 50%;
+  z-index: 100;
+
+  &:hover, &:active {
+    &::before, &::after {
+      display: block;
+    }
+  }
+
+  &::before {
+    content: attr(data-tooltip);
+    min-width: 28px;
+    display: none;
+    position: absolute;
+    left: 50%;
+    bottom: 24px;
+    transform: translateX(-50%);
+    background-color: #262626;
+    text-align: center;
+    color: #fff;
+    border-radius: $borderRadius;
+    padding: 6px 5px;
+    font-size: 12px;
+  }
+  &::after {
+    content: '';
+    display: none;
+    position: absolute;
+    left: 50%;
+    bottom: 15px;
+    transform: translateX(-50%);
+    border: 5px solid transparent;
+    border-top-color: #262626;
+  }
+}
+</style>

+ 84 - 0
src/components/Switch.vue

@@ -0,0 +1,84 @@
+<template>
+  <span
+    class="switch"
+    :class="{
+      'active': value,
+      'disabled': disabled,
+    }" 
+    @click="handleChange()"
+  >
+    <span class="switch-core"></span>
+  </span>
+</template>
+
+<script lang="ts" setup>
+const props = withDefaults(defineProps<{
+  value: boolean
+  disabled?: boolean
+}>(), {
+  disabled: false,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: boolean): void
+}>()
+
+const handleChange = () => {
+  if (props.disabled) return
+  emit('update:value', !props.value)
+}
+</script>
+
+<style lang="scss" scoped>
+.switch {
+  height: 20px;
+  display: inline-block;
+  cursor: pointer;
+
+  &:not(.disabled).active {
+    .switch-core {
+      border-color: $themeColor;
+      background-color: $themeColor;
+
+      &::after {
+        left: 100%;
+        margin-left: -17px;
+      }
+    }
+  }
+
+  &.disabled {
+    cursor: default;
+
+    .switch-core::after {
+      background-color: #f5f5f5;
+    }
+  }
+}
+.switch-core {
+  margin: 0;
+  display: inline-block;
+  position: relative;
+  width: 40px;
+  height: 20px;
+  border: 1px solid #d9d9d9;
+  outline: none;
+  border-radius: 10px;
+  box-sizing: border-box;
+  background: #d9d9d9;
+  transition: border-color .3s, background-color .3s;
+  vertical-align: middle;
+
+  &::after {
+    content: '';
+    position: absolute;
+    top: 1px;
+    left: 1px;
+    border-radius: 100%;
+    transition: all .3s;
+    width: 16px;
+    height: 16px;
+    background-color: #fff;
+  }
+}
+</style>

+ 108 - 0
src/components/Tabs.vue

@@ -0,0 +1,108 @@
+<template>
+  <div class="tabs"
+    :class="{
+      'card': card,
+      'space-around': spaceAround,
+      'space-between': spaceBetween,
+    }" 
+    :style="tabsStyle || {}"
+  >
+    <div 
+      class="tab" 
+      :class="{ 'active': tab.key === value }"
+      v-for="tab in tabs" 
+      :key="tab.key"
+      :style="{
+        ...(tabStyle || {}),
+        '--color': tab.color,
+      }"
+      @click="emit('update:value', tab.key)"
+    >{{tab.label}}</div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { type CSSProperties } from 'vue'
+
+interface TabItem {
+  key: string
+  label: string
+  color?: string
+}
+
+withDefaults(defineProps<{
+  value: string
+  tabs: TabItem[]
+  card?: boolean
+  tabsStyle?: CSSProperties
+  tabStyle?: CSSProperties
+  spaceAround?: boolean
+  spaceBetween?: boolean
+}>(), {
+  card: false,
+  spaceAround: false,
+  spaceBetween: false,
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: string): void
+}>()
+</script>
+
+<style lang="scss" scoped>
+.tabs {
+  display: flex;
+  user-select: none;
+  line-height: 1;
+
+  &:not(.card) {
+    font-size: 13px;
+    align-items: center;
+    justify-content: flex-start;
+    border-bottom: 1px solid $borderColor;
+
+    &.space-around {
+      justify-content: space-around;
+    }
+    &.space-between {
+      justify-content: space-between;
+    }
+
+    .tab {
+      text-align: center;
+      border-bottom: 2px solid transparent;
+      padding: 8px 10px;
+      cursor: pointer;
+
+      &.active {
+        border-bottom: 2px solid var(--color, $themeColor);
+      }
+    }
+  }
+
+  &.card {
+    height: 40px;
+    font-size: 12px;
+    flex-shrink: 0;
+
+    .tab {
+      flex: 1;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      background-color: $lightGray;
+      border-bottom: 1px solid $borderColor;
+      cursor: pointer;
+
+      &.active {
+        background-color: transparent;
+        border-bottom-color: transparent;
+      }
+
+      & + .tab {
+        border-left: 1px solid $borderColor;
+      }
+    }
+  }
+}
+</style>

+ 92 - 0
src/components/TextArea.vue

@@ -0,0 +1,92 @@
+<template>
+  <textarea
+    class="textarea" 
+    :class="{
+      'disabled': disabled,
+      'resizable': resizable,
+    }"
+    ref="textareaRef"
+    :disabled="disabled"
+    :value="value" 
+    :rows="rows"
+    :placeholder="placeholder"
+    :style="{
+      padding: padding ? `${padding}px` : '10px',
+    }"
+    @input="$event => handleInput($event)"
+    @focus="$event => emit('focus', $event)"
+    @blur="$event => emit('blur', $event)"
+  ></textarea>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+
+withDefaults(defineProps<{
+  value: string
+  rows?: number
+  padding?: number
+  disabled?: boolean
+  resizable?: boolean
+  placeholder?: string
+}>(), {
+  rows: 4,
+  disabled: false,
+  resizable: false,
+  placeholder: '',
+})
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: string): void
+  (event: 'focus', payload: FocusEvent): void
+  (event: 'blur', payload: FocusEvent): void
+}>()
+
+const handleInput = (e: Event) => {
+  emit('update:value', (e.target as HTMLInputElement).value)
+}
+
+const textareaRef = ref<HTMLTextAreaElement>()
+const focus = () => {
+  if (textareaRef.value) textareaRef.value.focus()
+}
+
+defineExpose({
+  focus,
+})
+</script>
+
+<style lang="scss" scoped>
+.textarea {
+  outline: 0;
+  width: 100%;
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: $borderRadius;
+  padding: 10px;
+  transition: border-color .25s;
+  box-sizing: border-box;
+  line-height: 1.675;
+  resize: none;
+  font-family: -apple-system,BlinkMacSystemFont, 'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji';
+
+  &:focus {
+    border-color: $themeColor;
+    background-color: #fff;
+  }
+
+  &.resizable {
+    resize: vertical;
+  }
+
+  &.disabled {
+    background-color: #f5f5f5;
+    border-color: #dcdcdc;
+    color: #b7b7b7;
+  }
+
+  &::placeholder {
+    color: #bfbfbf;
+  }
+}
+</style>

+ 38 - 0
src/components/TextColorButton.vue

@@ -0,0 +1,38 @@
+<template>
+  <Button class="text-color-btn">
+    <slot></slot>
+    <div class="text-color-block">
+      <div class="text-color-block-content" :style="{ backgroundColor: color }"></div>
+    </div>
+  </Button>
+</template>
+
+<script lang="ts" setup>
+import Button from './Button.vue'
+
+defineProps<{
+  color: string
+}>()
+</script>
+
+<style lang="scss" scoped>
+.text-color-btn {
+  width: 100%;
+  display: flex !important;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  padding: 0;
+}
+.text-color-block {
+  width: 17px;
+  height: 4px;
+  margin-top: 1px;
+  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAACdJREFUGFdjfPbs2X8GBgYGSUlJEMXAiCHw//9/sIrnz59DVKALAADNxxVfaiODNQAAAABJRU5ErkJggg==);
+
+  .text-color-block-content {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 359 - 0
src/components/WritingBoard.vue

@@ -0,0 +1,359 @@
+<template>
+  <div class="writing-board" ref="writingBoardRef">
+    <div class="blackboard" v-if="blackboard"></div>
+
+    <canvas class="canvas" ref="canvasRef"
+      :style="{
+        width: canvasWidth + 'px',
+        height: canvasHeight + 'px',
+      }"
+      @mousedown="$event => handleMousedown($event)"
+      @mousemove="$event => handleMousemove($event)"
+      @mouseup="handleMouseup()"
+      @touchstart="$event => handleMousedown($event)"
+      @touchmove="$event => handleMousemove($event)"
+      @touchend="handleMouseup(); mouseInCanvas = false"
+      @mouseleave="handleMouseup(); mouseInCanvas = false"
+      @mouseenter="mouseInCanvas = true"
+    ></canvas>
+
+    <template v-if="mouseInCanvas">
+      <div 
+        class="eraser"
+        :style="{
+          left: mouse.x - rubberSize / 2 + 'px',
+          top: mouse.y - rubberSize / 2 + 'px',
+          width: rubberSize + 'px',
+          height: rubberSize + 'px',
+        }"
+        v-if="model === 'eraser'"
+      ></div>
+      <div 
+        class="pen"
+        :style="{
+          left: mouse.x - penSize / 2 + 'px',
+          top: mouse.y - penSize * 6 + penSize / 2 + 'px',
+          color: color,
+        }"
+        v-if="model === 'pen'"
+      >
+        <IconWrite class="icon" :size="penSize * 6" v-if="model === 'pen'" />
+      </div>
+      <div 
+        class="pen"
+        :style="{
+          left: mouse.x - markSize / 2 + 'px',
+          top: mouse.y + 'px',
+          color: color,
+        }"
+        v-if="model === 'mark'"
+      >
+        <IconHighLight class="icon" :size="markSize * 1.5" v-if="model === 'mark'" />
+      </div>
+    </template>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
+
+const props = withDefaults(defineProps<{
+  color?: string
+  model?: 'pen' | 'eraser' | 'mark'
+  blackboard?: boolean
+  penSize?: number
+  markSize?: number
+  rubberSize?: number
+}>(), {
+  color: '#ffcc00',
+  model: 'pen',
+  blackboard: false,
+  penSize: 6,
+  markSize: 24,
+  rubberSize: 80,
+})
+
+const emit = defineEmits<{
+  (event: 'end'): void
+}>()
+
+let ctx: CanvasRenderingContext2D | null = null
+const writingBoardRef = ref<HTMLElement>()
+const canvasRef = ref<HTMLCanvasElement>()
+
+let lastPos = {
+  x: 0,
+  y: 0,
+}
+let isMouseDown = false
+let lastTime = 0
+let lastLineWidth = -1
+
+// 鼠标位置坐标:用于画笔或橡皮位置跟随
+const mouse = ref({
+  x: 0,
+  y: 0,
+})
+
+// 鼠标是否处在画布范围内:处在范围内才会显示画笔或橡皮
+const mouseInCanvas = ref(false)
+
+// 监听更新canvas尺寸
+const canvasWidth = ref(0)
+const canvasHeight = ref(0)
+
+const widthScale = computed(() => canvasRef.value ? canvasWidth.value / canvasRef.value.width : 1)
+const heightScale = computed(() => canvasRef.value ? canvasHeight.value / canvasRef.value.height : 1)
+
+const updateCanvasSize = () => {
+  if (!writingBoardRef.value) return
+  canvasWidth.value = writingBoardRef.value.clientWidth
+  canvasHeight.value = writingBoardRef.value.clientHeight
+}
+const resizeObserver = new ResizeObserver(updateCanvasSize)
+onMounted(() => {
+  if (writingBoardRef.value) resizeObserver.observe(writingBoardRef.value)
+})
+onUnmounted(() => {
+  if (writingBoardRef.value) resizeObserver.unobserve(writingBoardRef.value)
+})
+
+// 初始化画布
+const initCanvas = () => {
+  if (!canvasRef.value || !writingBoardRef.value) return
+
+  ctx = canvasRef.value.getContext('2d')
+  if (!ctx) return
+
+  canvasRef.value.width = writingBoardRef.value.clientWidth
+  canvasRef.value.height = writingBoardRef.value.clientHeight
+
+  ctx.lineCap = 'round'
+  ctx.lineJoin = 'round'
+}
+onMounted(initCanvas)
+
+// 切换画笔模式时,更新 canvas ctx 配置
+const updateCtx = () => {
+  if (!ctx) return
+  if (props.model === 'mark') {
+    ctx.globalCompositeOperation = 'xor'
+    ctx.globalAlpha = 0.5
+  }
+  else if (props.model === 'pen') {
+    ctx.globalCompositeOperation = 'source-over'
+    ctx.globalAlpha = 1
+  }
+}
+watch(() => props.model, updateCtx)
+
+// 绘制画笔墨迹方法
+const draw = (posX: number, posY: number, lineWidth: number) => {
+  if (!ctx) return
+
+  const lastPosX = lastPos.x
+  const lastPosY = lastPos.y
+
+  ctx.lineWidth = lineWidth
+  ctx.strokeStyle = props.color
+  ctx.beginPath()
+  ctx.moveTo(lastPosX, lastPosY)
+  ctx.lineTo(posX, posY)
+  ctx.stroke()
+  ctx.closePath()
+}
+
+// 擦除墨迹方法
+const erase = (posX: number, posY: number) => {
+  if (!ctx || !canvasRef.value) return
+  const lastPosX = lastPos.x
+  const lastPosY = lastPos.y
+
+  const radius = props.rubberSize / 2
+
+  const sinRadius = radius * Math.sin(Math.atan((posY - lastPosY) / (posX - lastPosX)))
+  const cosRadius = radius * Math.cos(Math.atan((posY - lastPosY) / (posX - lastPosX)))
+  const rectPoint1: [number, number] = [lastPosX + sinRadius, lastPosY - cosRadius]
+  const rectPoint2: [number, number] = [lastPosX - sinRadius, lastPosY + cosRadius]
+  const rectPoint3: [number, number] = [posX + sinRadius, posY - cosRadius]
+  const rectPoint4: [number, number] = [posX - sinRadius, posY + cosRadius]
+
+  ctx.save()
+  ctx.beginPath()
+  ctx.arc(posX, posY, radius, 0, Math.PI * 2)
+  ctx.clip()
+  ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
+  ctx.restore()
+
+  ctx.save()
+  ctx.beginPath()
+  ctx.moveTo(...rectPoint1)
+  ctx.lineTo(...rectPoint3)
+  ctx.lineTo(...rectPoint4)
+  ctx.lineTo(...rectPoint2)
+  ctx.closePath()
+  ctx.clip()
+  ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
+  ctx.restore()
+}
+
+// 计算鼠标两次移动之间的距离
+const getDistance = (posX: number, posY: number) => {
+  const lastPosX = lastPos.x
+  const lastPosY = lastPos.y
+  return Math.sqrt((posX - lastPosX) * (posX - lastPosX) + (posY - lastPosY) * (posY - lastPosY))
+}
+
+// 根据鼠标两次移动之间的距离s和时间t计算绘制速度,速度越快,墨迹越细
+const getLineWidth = (s: number, t: number) => {
+  const maxV = 10
+  const minV = 0.1
+  const maxWidth = props.penSize
+  const minWidth = 3
+  const v = s / t
+  let lineWidth
+
+  if (v <= minV) lineWidth = maxWidth
+  else if (v >= maxV) lineWidth = minWidth
+  else lineWidth = maxWidth - v / maxV * maxWidth
+
+  if (lastLineWidth === -1) return lineWidth
+  return lineWidth * 1 / 3 + lastLineWidth * 2 / 3
+}
+
+// 路径操作
+const handleMove = (x: number, y: number) => {
+  const time = new Date().getTime()
+
+  if (props.model === 'pen') {
+    const s = getDistance(x, y)
+    const t = time - lastTime
+    const lineWidth = getLineWidth(s, t)
+
+    draw(x, y, lineWidth)
+    lastLineWidth = lineWidth
+  }
+  else if (props.model === 'mark') draw(x, y, props.markSize)
+  else erase(x, y)
+
+  lastPos = { x, y }
+  lastTime = new Date().getTime()
+}
+
+// 获取鼠标在canvas中的相对位置
+const getMouseOffsetPosition = (e: MouseEvent | TouchEvent) => {
+  if (!canvasRef.value) return [0, 0]
+  const event = e instanceof MouseEvent ? e : e.changedTouches[0]
+  const canvasRect = canvasRef.value.getBoundingClientRect()
+  const x = event.pageX - canvasRect.x
+  const y = event.pageY - canvasRect.y
+  return [x, y]
+}
+
+// 处理鼠标(触摸)事件
+// 准备开始绘制/擦除墨迹(落笔)
+const handleMousedown = (e: MouseEvent | TouchEvent) => {
+  const [mouseX, mouseY] = getMouseOffsetPosition(e)
+  const x = mouseX / widthScale.value
+  const y = mouseY / heightScale.value
+
+  isMouseDown = true
+  lastPos = { x, y }
+  lastTime = new Date().getTime()
+
+  if (!(e instanceof MouseEvent)) {
+    mouse.value = { x: mouseX, y: mouseY }
+    mouseInCanvas.value = true
+  }
+}
+
+// 开始绘制/擦除墨迹(移动)
+const handleMousemove = (e: MouseEvent | TouchEvent) => {
+  const [mouseX, mouseY] = getMouseOffsetPosition(e)
+  const x = mouseX / widthScale.value
+  const y = mouseY / heightScale.value
+
+  mouse.value = { x: mouseX, y: mouseY }
+
+  if (isMouseDown) handleMove(x, y)
+}
+
+// 结束绘制/擦除墨迹(停笔)
+const handleMouseup = () => {
+  if (!isMouseDown) return
+  isMouseDown = false
+  emit('end')
+}
+
+// 清空画布
+const clearCanvas = () => {
+  if (!ctx || !canvasRef.value) return
+  ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
+  emit('end')
+}
+
+// 获取 DataURL
+const getImageDataURL = () => {
+  return canvasRef.value?.toDataURL()
+}
+
+// 设置 DataURL(绘制图片到 canvas)
+const setImageDataURL = (imageDataURL: string) => {
+  if (!ctx || !canvasRef.value) return
+  
+  ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
+
+  if (imageDataURL) {
+    ctx.globalCompositeOperation = 'source-over'
+    ctx.globalAlpha = 1
+
+    const img = new Image()
+    img.src = imageDataURL
+    img.onload = () => {
+      ctx!.drawImage(img, 0, 0)
+      updateCtx()
+    }
+  }
+}
+
+defineExpose({
+  clearCanvas,
+  getImageDataURL,
+  setImageDataURL,
+})
+</script>
+
+<style lang="scss" scoped>
+.writing-board {
+  z-index: 8;
+  cursor: none;
+  @include absolute-0();
+}
+.blackboard {
+  width: 100%;
+  height: 100%;
+  background-color: #0f392b;
+}
+.canvas {
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+.eraser, .pen {
+  pointer-events: none;
+  position: absolute;
+  z-index: 9;
+
+  .icon {
+    filter: drop-shadow(2px 2px 2px #555);
+  }
+}
+.eraser {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+  border: 4px solid rgba($color: #555, $alpha: .15);
+  color: rgba($color: #555, $alpha: .75);
+}
+</style>

+ 234 - 0
src/configs/animation.ts

@@ -0,0 +1,234 @@
+import type { TurningMode } from '@/types/slides'
+
+export const ANIMATION_DEFAULT_DURATION = 1000
+export const ANIMATION_DEFAULT_TRIGGER = 'click'
+export const ANIMATION_CLASS_PREFIX = 'animate__'
+
+export const ENTER_ANIMATIONS = [
+  {
+    type: 'bounce',
+    name: '弹跳',
+    children: [
+      { name: '弹入', value: 'bounceIn' },
+      { name: '向右弹入', value: 'bounceInLeft' },
+      { name: '向左弹入', value: 'bounceInRight' },
+      { name: '向上弹入', value: 'bounceInUp' },
+      { name: '向下弹入', value: 'bounceInDown' },
+    ],
+  },
+  {
+    type: 'fade',
+    name: '浮现',
+    children: [
+      { name: '浮入', value: 'fadeIn' },
+      { name: '向下浮入', value: 'fadeInDown' },
+      { name: '向下长距浮入', value: 'fadeInDownBig' },
+      { name: '向右浮入', value: 'fadeInLeft' },
+      { name: '向右长距浮入', value: 'fadeInLeftBig' },
+      { name: '向左浮入', value: 'fadeInRight' },
+      { name: '向左长距浮入', value: 'fadeInRightBig' },
+      { name: '向上浮入', value: 'fadeInUp' },
+      { name: '向上长距浮入', value: 'fadeInUpBig' },
+      { name: '从左上浮入', value: 'fadeInTopLeft' },
+      { name: '从右上浮入', value: 'fadeInTopRight' },
+      { name: '从左下浮入', value: 'fadeInBottomLeft' },
+      { name: '从右下浮入', value: 'fadeInBottomRight' },
+    ],
+  },
+  {
+    type: 'rotate',
+    name: '旋转',
+    children: [
+      { name: '旋转进入', value: 'rotateIn' },
+      { name: '绕左下进入', value: 'rotateInDownLeft' },
+      { name: '绕右下进入', value: 'rotateInDownRight' },
+      { name: '绕左上进入', value: 'rotateInUpLeft' },
+      { name: '绕右上进入', value: 'rotateInUpRight' },
+    ],
+  },
+  {
+    type: 'zoom',
+    name: '缩放',
+    children: [
+      { name: '放大进入', value: 'zoomIn' },
+      { name: '向下放大进入', value: 'zoomInDown' },
+      { name: '从左放大进入', value: 'zoomInLeft' },
+      { name: '从右放大进入', value: 'zoomInRight' },
+      { name: '向上放大进入', value: 'zoomInUp' },
+    ],
+  },
+  {
+    type: 'slide',
+    name: '滑入',
+    children: [
+      { name: '向下滑入', value: 'slideInDown' },
+      { name: '从右滑入', value: 'slideInLeft' },
+      { name: '从左滑入', value: 'slideInRight' },
+      { name: '向上滑入', value: 'slideInUp' },
+    ],
+  },
+  {
+    type: 'flip',
+    name: '翻转',
+    children: [
+      { name: 'X轴翻转进入', value: 'flipInX' },
+      { name: 'Y轴翻转进入', value: 'flipInY' },
+    ],
+  },
+  {
+    type: 'back',
+    name: '放大滑入',
+    children: [
+      { name: '向下放大滑入', value: 'backInDown' },
+      { name: '从左放大滑入', value: 'backInLeft' },
+      { name: '从右放大滑入', value: 'backInRight' },
+      { name: '向上放大滑入', value: 'backInUp' },
+    ],
+  },
+  {
+    type: 'lightSpeed',
+    name: '飞入',
+    children: [
+      { name: '从右飞入', value: 'lightSpeedInRight' },
+      { name: '从左飞入', value: 'lightSpeedInLeft' },
+    ],
+  },
+]
+
+export const EXIT_ANIMATIONS = [
+  {
+    type: 'bounce',
+    name: '弹跳',
+    children: [
+      { name: '弹出', value: 'bounceOut' },
+      { name: '向左弹出', value: 'bounceOutLeft' },
+      { name: '向右弹出', value: 'bounceOutRight' },
+      { name: '向上弹出', value: 'bounceOutUp' },
+      { name: '向下弹出', value: 'bounceOutDown' },
+    ],
+  },
+  {
+    type: 'fade',
+    name: '浮现',
+    children: [
+      { name: '浮出', value: 'fadeOut' },
+      { name: '向下浮出', value: 'fadeOutDown' },
+      { name: '向下长距浮出', value: 'fadeOutDownBig' },
+      { name: '向左浮出', value: 'fadeOutLeft' },
+      { name: '向左长距浮出', value: 'fadeOutLeftBig' },
+      { name: '向右浮出', value: 'fadeOutRight' },
+      { name: '向右长距浮出', value: 'fadeOutRightBig' },
+      { name: '向上浮出', value: 'fadeOutUp' },
+      { name: '向上长距浮出', value: 'fadeOutUpBig' },
+      { name: '从左上浮出', value: 'fadeOutTopLeft' },
+      { name: '从右上浮出', value: 'fadeOutTopRight' },
+      { name: '从左下浮出', value: 'fadeOutBottomLeft' },
+      { name: '从右下浮出', value: 'fadeOutBottomRight' },
+    ],
+  },
+  {
+    type: 'rotate',
+    name: '旋转',
+    children: [
+      { name: '旋转退出', value: 'rotateOut' },
+      { name: '绕左下退出', value: 'rotateOutDownLeft' },
+      { name: '绕右下退出', value: 'rotateOutDownRight' },
+      { name: '绕左上退出', value: 'rotateOutUpLeft' },
+      { name: '绕右上退出', value: 'rotateOutUpRight' },
+    ],
+  },
+  {
+    type: 'zoom',
+    name: '缩放',
+    children: [
+      { name: '缩小退出', value: 'zoomOut' },
+      { name: '向下缩小退出', value: 'zoomOutDown' },
+      { name: '从左缩小退出', value: 'zoomOutLeft' },
+      { name: '从右缩小退出', value: 'zoomOutRight' },
+      { name: '向上缩小退出', value: 'zoomOutUp' },
+    ],
+  },
+  {
+    type: 'slide',
+    name: '滑出',
+    children: [
+      { name: '向下滑出', value: 'slideOutDown' },
+      { name: '从左滑出', value: 'slideOutLeft' },
+      { name: '从右滑出', value: 'slideOutRight' },
+      { name: '向上滑出', value: 'slideOutUp' },
+    ],
+  },
+  {
+    type: 'flip',
+    name: '翻转',
+    children: [
+      { name: 'X轴翻转退出', value: 'flipOutX' },
+      { name: 'Y轴翻转退出', value: 'flipOutY' },
+    ],
+  },
+  {
+    type: 'back',
+    name: '缩小滑出',
+    children: [
+      { name: '向下缩小滑出', value: 'backOutDown' },
+      { name: '从左缩小滑出', value: 'backOutLeft' },
+      { name: '从右缩小滑出', value: 'backOutRight' },
+      { name: '向上缩小滑出', value: 'backOutUp' },
+    ],
+  },
+  {
+    type: 'lightSpeed',
+    name: '飞出',
+    children: [
+      { name: '从右飞出', value: 'lightSpeedOutRight' },
+      { name: '从左飞出', value: 'lightSpeedOutLeft' },
+    ],
+  },
+]
+
+export const ATTENTION_ANIMATIONS = [
+  {
+    type: 'shake',
+    name: '晃动',
+    children: [
+      { name: '左右摇晃', value: 'shakeX' },
+      { name: '上下摇晃', value: 'shakeY' },
+      { name: '摇头', value: 'headShake' },
+      { name: '摆动', value: 'swing' },
+      { name: '晃动', value: 'wobble' },
+      { name: '惊恐', value: 'tada' },
+      { name: '果冻', value: 'jello' },
+    ],
+  },
+  {
+    type: 'other',
+    name: '其他',
+    children: [
+      { name: '弹跳', value: 'bounce' },
+      { name: '闪烁', value: 'flash' },
+      { name: '脉搏', value: 'pulse' },
+      { name: '橡皮筋', value: 'rubberBand' },
+      { name: '心跳(快)', value: 'heartBeat' },
+    ],
+  },
+]
+
+interface SlideAnimation {
+  label: string
+  value: TurningMode
+}
+
+export const SLIDE_ANIMATIONS: SlideAnimation[] = [
+  { label: '无', value: 'no' },
+  { label: '随机', value: 'random' },
+  { label: '左右推移', value: 'slideX' },
+  { label: '上下推移', value: 'slideY' },
+  { label: '左右推移(3D)', value: 'slideX3D' },
+  { label: '上下推移(3D)', value: 'slideY3D' },
+  { label: '淡入淡出', value: 'fade' },
+  { label: '旋转', value: 'rotate' },
+  { label: '上下展开', value: 'scaleY' },
+  { label: '左右展开', value: 'scaleX' },
+  { label: '放大', value: 'scale' },
+  { label: '缩小', value: 'scaleReverse' },
+]

+ 70 - 0
src/configs/chart.ts

@@ -0,0 +1,70 @@
+import type { ChartData } from '@/types/slides'
+
+export const CHART_TYPE_MAP: { [key: string]: string } = {
+  'bar': '柱状图',
+  'column': '条形图',
+  'line': '折线图',
+  'area': '面积图',
+  'scatter': '散点图',
+  'pie': '饼图',
+  'ring': '环形图',
+  'radar': '雷达图',
+}
+
+export const CHART_DEFAULT_DATA: { [key: string]: ChartData } = {
+  'bar': {
+    labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],
+    legends: ['系列1', '系列2'],
+    series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]],
+  },
+  'column': {
+    labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],
+    legends: ['系列1', '系列2'],
+    series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]],
+  },
+  'line': {
+    labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],
+    legends: ['系列1', '系列2'],
+    series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]],
+  },
+  'pie': {
+    labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],
+    legends: ['值'],
+    series: [[12, 19, 5, 2, 18]],
+  },
+  'ring': {
+    labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],
+    legends: ['值'],
+    series: [[12, 19, 5, 2, 18]],
+  },
+  'area': {
+    labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],
+    legends: ['系列1', '系列2'],
+    series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]],
+  },
+  'radar': {
+    labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],
+    legends: ['系列1', '系列2'],
+    series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]],
+  },
+  'scatter': {
+    labels: ['坐标1', '坐标2', '坐标3', '坐标4', '坐标5'],
+    legends: ['X', 'Y'],
+    series: [[12, 19, 5, 2, 18], [7, 11, 13, 21, 9]],
+  },
+}
+
+export const CHART_PRESET_THEMES = [
+  ['#d87c7c', '#919e8b', '#d7ab82', '#6e7074', '#61a0a8', '#efa18d'],
+  ['#dd6b66', '#759aa0', '#e69d87', '#8dc1a9', '#ea7e53', '#eedd78'],
+  ['#516b91', '#59c4e6', '#edafda', '#93b7e3', '#a5e7f0', '#cbb0e3'],
+  ['#893448', '#d95850', '#eb8146', '#ffb248', '#f2d643', '#ebdba4'],
+  ['#4ea397', '#22c3aa', '#7bd9a5', '#d0648a', '#f58db2', '#f2b3c9'],
+  ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
+  ['#fc97af', '#87f7cf', '#f7f494', '#72ccff', '#f7c5a0', '#d4a4eb'],
+  ['#c1232b', '#27727b', '#fcce10', '#e87c25', '#b5c334', '#fe8463'],
+  ['#2ec7c9', '#b6a2de', '#5ab1ef', '#ffb980', '#d87a80', '#8d98b3'],
+  ['#e01f54', '#001852', '#f5e8c8', '#b8d2c7', '#c6b38e', '#a4d8c2'],
+  ['#c12e34', '#e6b600', '#0098d9', '#2b821d', '#005eaa', '#339ca8'],
+  ['#8a7ca8', '#e098c7', '#8fd3e8', '#71669e', '#cc70af', '#7cb4cc'],
+]

+ 22 - 0
src/configs/element.ts

@@ -0,0 +1,22 @@
+export const ELEMENT_TYPE_ZH: { [key: string]: string } = {
+  text: '文本',
+  image: '图片',
+  shape: '形状',
+  line: '线条',
+  chart: '图表',
+  table: '表格',
+  video: '视频',
+  audio: '音频',
+  latex: '公式',
+}
+
+export const MIN_SIZE: { [key: string]: number } = {
+  text: 20,
+  image: 20,
+  shape: 20,
+  chart: 200,
+  table: 20,
+  video: 250,
+  audio: 20,
+  latex: 20,
+}

+ 44 - 0
src/configs/font.ts

@@ -0,0 +1,44 @@
+export const SYS_FONTS = [
+  { label: 'Arial', value: 'Arial' },
+  { label: '微软雅黑', value: 'Microsoft Yahei' },
+  { label: '宋体', value: 'SimSun' },
+  { label: '黑体', value: 'SimHei' },
+  { label: '楷体', value: 'KaiTi' },
+  { label: '新宋体', value: 'NSimSun' },
+  { label: '仿宋', value: 'FangSong' },
+  { label: '苹方', value: 'PingFang SC' },
+  { label: '华文黑体', value: 'STHeiti' },
+  { label: '华文楷体', value: 'STKaiti' },
+  { label: '华文宋体', value: 'STSong' },
+  { label: '华文仿宋', value: 'STFangSong' },
+  { label: '华文中宋', value: 'STZhongSong' },
+  { label: '华文琥珀', value: 'STHupo' },
+  { label: '华文新魏', value: 'STXinwei' },
+  { label: '华文隶书', value: 'STLiti' },
+  { label: '华文行楷', value: 'STXingkai' },
+  { label: '冬青黑体', value: 'Hiragino Sans GB' },
+  { label: '兰亭黑', value: 'Lantinghei SC' },
+  { label: '偏偏体', value: 'Hanzipen SC' },
+  { label: '手札体', value: 'Hannotate SC' },
+  { label: '宋体', value: 'Songti SC' },
+  { label: '娃娃体', value: 'Wawati SC' },
+  { label: '行楷', value: 'Xingkai SC' },
+  { label: '圆体', value: 'Yuanti SC' },
+  { label: '华文细黑', value: 'STXihei' },
+  { label: '幼圆', value: 'YouYuan' },
+  { label: '隶书', value: 'LiSu' },
+]
+
+export const WEB_FONTS = [
+  { label: '得意黑', value: '得意黑' },
+  { label: '仓耳小丸子', value: '仓耳小丸子' },
+  { label: '优设标题黑', value: '优设标题黑' },
+  { label: '峰广明锐体', value: '峰广明锐体' },
+  { label: '摄图摩登小方体', value: '摄图摩登小方体' },
+  { label: '站酷快乐体', value: '站酷快乐体' },
+  { label: '字制区喜脉体', value: '字制区喜脉体' },
+  { label: '素材集市康康体', value: '素材集市康康体' },
+  { label: '素材集市酷方体', value: '素材集市酷方体' },
+  { label: '途牛类圆体', value: '途牛类圆体' },
+  { label: '锐字真言体', value: '锐字真言体' },
+]

+ 129 - 0
src/configs/hotkey.ts

@@ -0,0 +1,129 @@
+export const enum KEYS {
+  C = 'C',
+  X = 'X',
+  Z = 'Z',
+  Y = 'Y',
+  A = 'A',
+  G = 'G',
+  L = 'L',
+  F = 'F',
+  D = 'D',
+  B = 'B',
+  P = 'P',
+  O = 'O',
+  R = 'R',
+  T = 'T',
+  MINUS = '-',
+  EQUAL = '=',
+  DIGIT_0 = '0',
+  DELETE = 'DELETE',
+  UP = 'ARROWUP',
+  DOWN = 'ARROWDOWN',
+  LEFT = 'ARROWLEFT',
+  RIGHT = 'ARROWRIGHT',
+  ENTER = 'ENTER',
+  SPACE = ' ',
+  TAB = 'TAB',
+  BACKSPACE = 'BACKSPACE',
+  ESC = 'ESCAPE',
+  PAGEUP = 'PAGEUP',
+  PAGEDOWN = 'PAGEDOWN',
+  F5 = 'F5',
+}
+
+export const HOTKEY_DOC = [
+  {
+    type: '通用',
+    children: [
+      { label: '剪切', value: 'Ctrl + X' },
+      { label: '复制', value: 'Ctrl + C' },
+      { label: '粘贴', value: 'Ctrl + V' },
+      { label: '粘贴为纯文本', value: 'Ctrl + Shift + V' },
+      { label: '快速复制粘贴', value: 'Ctrl + D' },
+      { label: '全选', value: 'Ctrl + A' },
+      { label: '撤销', value: 'Ctrl + Z' },
+      { label: '恢复', value: 'Ctrl + Y' },
+      { label: '删除', value: 'Delete / Backspace' },
+      { label: '多选', value: '按住 Ctrl 或 Shift' },
+      { label: '打开搜索替换', value: 'Ctrl + F' },
+      { label: '打印', value: 'Ctrl + P' },
+      { label: '关闭弹窗', value: 'ESC' },
+    ],
+  },
+  {
+    type: '幻灯片放映',
+    children: [
+      { label: '从头开始放映幻灯片', value: 'F5' },
+      { label: '从当前开始放映幻灯片', value: 'Shift + F5' },
+      { label: '切换上一页', value: '↑ / ← / PgUp' },
+      { label: '切换下一页', value: '↓ / → / PgDown' },
+      { label: '切换下一页', value: 'Enter / Space' },
+      { label: '退出放映', value: 'ESC' },
+    ],
+  },
+  {
+    type: '幻灯片编辑',
+    children: [
+      { label: '新建幻灯片', value: 'Enter' },
+      { label: '移动画布', value: 'Space + 鼠标拖拽' },
+      { label: '缩放画布', value: 'Ctrl + 鼠标滚轮' },
+      { label: '放大画布', value: 'Ctrl + =' },
+      { label: '缩小画布', value: 'Ctrl + -' },
+      { label: '使画布适应当前屏幕', value: 'Ctrl + 0' },
+      { label: '上一页(未选中元素)', value: '↑' },
+      { label: '下一页(未选中元素)', value: '↓' },
+      { label: '上一页', value: '鼠标上滚 / PgUp' },
+      { label: '下一页', value: '鼠标下滚 / PgDown' },
+      { label: '快速创建文本', value: '双击空白处 / T' },
+      { label: '快速创建矩形', value: 'R' },
+      { label: '快速创建圆形', value: 'O' },
+      { label: '快速创建线条', value: 'L' },
+      { label: '退出绘制状态', value: '鼠标右键' },
+    ],
+  },
+  {
+    type: '元素操作',
+    children: [
+      { label: '移动', value: '↑ / ← / ↓ / →' },
+      { label: '锁定', value: 'Ctrl + L' },
+      { label: '组合', value: 'Ctrl + G' },
+      { label: '取消组合', value: 'Ctrl + Shift + G' },
+      { label: '置顶层', value: 'Alt + F' },
+      { label: '置底层', value: 'Alt + B' },
+      { label: '锁定宽高比例', value: '按住 Ctrl 或 Shift' },
+      { label: '创建水平 / 垂直线条', value: '按住 Ctrl 或 Shift' },
+      { label: '切换焦点元素', value: 'Tab' },
+      { label: '确认图片裁剪', value: 'Enter' },
+      { label: '完成自定义形状绘制', value: 'Enter' },
+    ],
+  },
+  {
+    type: '表格编辑',
+    children: [
+      { label: '聚焦到下一个单元格', value: 'Tab' },
+      { label: '移动焦点单元格', value: '↑ / ← / ↓ / →' },
+      { label: '在上方插入一行', value: 'Ctrl + ↑' },
+      { label: '在下方插入一行', value: 'Ctrl + ↓' },
+      { label: '在左侧插入一列', value: 'Ctrl + ←' },
+      { label: '在右侧插入一列', value: 'Ctrl + →' },
+    ],
+  },
+  {
+    type: '图表数据编辑',
+    children: [
+      { label: '聚焦到下一行', value: 'Enter' },
+    ],
+  },
+  {
+    type: '文本编辑',
+    children: [
+      { label: '加粗', value: 'Ctrl + B' },
+      { label: '斜体', value: 'Ctrl + I' },
+      { label: '下划线', value: 'Ctrl + U' },
+      { label: '行内代码', value: 'Ctrl + E' },
+      { label: '上角标', value: 'Ctrl + ;' },
+      { label: '下角标', value: `Ctrl + '` },
+      { label: '选中段落', value: `ESC` },
+    ],
+  },
+]

+ 181 - 0
src/configs/imageClip.ts

@@ -0,0 +1,181 @@
+export const enum ClipPathTypes {
+  RECT = 'rect',
+  ELLIPSE = 'ellipse',
+  POLYGON = 'polygon',
+}
+
+export const enum ClipPaths {
+  RECT = 'rect',
+  ROUNDRECT = 'roundRect',
+  ELLIPSE = 'ellipse',
+  TRIANGLE = 'triangle',
+  PENTAGON = 'pentagon',
+  RHOMBUS = 'rhombus',
+  STAR = 'star',
+}
+
+interface ClipPath {
+  [key: string]: {
+    name: string
+    type: ClipPathTypes
+    style: string
+    radius?: string
+    createPath?: (width: number, height: number) => string
+  }
+}
+
+export const CLIPPATHS: ClipPath = {
+  rect: {
+    name: '矩形',
+    type: ClipPathTypes.RECT,
+    radius: '0',
+    style: '',
+  },
+  rect2: {
+    name: '矩形2',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(0% 0%, 80% 0%, 100% 20%, 100% 100%, 0 100%)',
+    createPath: (width: number, height: number) => {
+      return `M 0 0 L ${width * 0.8} 0 L ${width} ${height * 0.2} L ${width} ${height} L 0 ${height} Z`
+    },
+  },
+  rect3: {
+    name: '矩形3',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(0% 0%, 80% 0%, 100% 20%, 100% 100%, 20% 100%, 0% 80%)',
+    createPath: (width: number, height: number) => {
+      return `M 0 0 L ${width * 0.8} 0 L ${width} ${height * 0.2} L ${width} ${height} L ${width * 0.2} ${height} L 0 ${height * 0.8} Z`
+    },
+  },
+  roundRect: {
+    name: '圆角矩形',
+    type: ClipPathTypes.RECT,
+    radius: '10px',
+    style: 'inset(0 round 10px)',
+  },
+  ellipse: {
+    name: '圆形',
+    type: ClipPathTypes.ELLIPSE,
+    style: 'ellipse(50% 50% at 50% 50%)',
+  },
+  triangle: {
+    name: '三角形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(50% 0%, 0% 100%, 100% 100%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.5} 0 L 0 ${height} L ${width} ${height} Z`
+    },
+  },
+  triangle2: {
+    name: '三角形2',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(50% 100%, 0% 0%, 100% 0%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.5} ${height} L 0 0 L ${width} 0 Z`
+    },
+  },
+  triangle3: {
+    name: '三角形3',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(0% 0%, 0% 100%, 100% 100%)',
+    createPath: (width: number, height: number) => {
+      return `M 0 0 L 0 ${height} L ${width} ${height} Z`
+    },
+  },
+  rhombus: {
+    name: '菱形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.5} 0 L ${width} ${height * 0.5} L ${width * 0.5} ${height} L 0 ${height * 0.5} Z`
+    },
+  },
+  pentagon: {
+    name: '五边形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.5} 0 L ${width} ${0.38 * height} L ${0.82 * width} ${height} L ${0.18 * width} ${height} L 0 ${0.38 * height} Z`
+    },
+  },
+  hexagon: {
+    name: '六边形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(20% 0%, 80% 0%, 100% 50%, 80% 100%, 20% 100%, 0% 50%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.2} 0 L ${width * 0.8} 0 L ${width} ${height * 0.5} L ${width * 0.8} ${height} L ${width * 0.2} ${height} L 0 ${height * 0.5} Z`
+    },
+  },
+  heptagon: {
+    name: '七边形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(50% 0%, 90% 20%, 100% 60%, 75% 100%, 25% 100%, 0% 60%, 10% 20%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.5} 0 L ${width * 0.9} ${height * 0.2} L ${width} ${height * 0.6} L ${width * 0.75} ${height} L ${width * 0.25} ${height} L 0 ${height * 0.6} L ${width * 0.1} ${height * 0.2} Z`
+    },
+  },
+  octagon: {
+    name: '八边形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.3} 0 L ${width * 0.7} 0 L ${width} ${height * 0.3} L ${width} ${height * 0.7} L ${width * 0.7} ${height} L ${width * 0.3} ${height} L 0 ${height * 0.7} L 0 ${height * 0.3} Z`
+    },
+  },
+  chevron: {
+    name: 'V形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 25% 50%, 0% 0%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.75} 0 L ${width} ${height * 0.5} L ${width * 0.75} ${height} L 0 ${height} L ${width * 0.25} ${height * 0.5} L 0 0 Z`
+    },
+  },
+  point: {
+    name: '点',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(0% 0%, 75% 0%, 100% 50%, 75% 100%, 0% 100%)',
+    createPath: (width: number, height: number) => {
+      return `M 0 0 L ${width * 0.75} 0 L ${width} ${height * 0.5} L ${width * 0.75} ${height} L 0 ${height} Z`
+    },
+  },
+  arrow: {
+    name: '箭头',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(0% 20%, 60% 20%, 60% 0%, 100% 50%, 60% 100%, 60% 80%, 0% 80%)',
+    createPath: (width: number, height: number) => {
+      return `M 0 ${height * 0.2} L ${width * 0.6} ${height * 0.2} L ${width * 0.6} 0 L ${width} ${height * 0.5} L ${width * 0.6} ${height} L ${width * 0.6} ${height * 0.8} L 0 ${height * 0.8} Z`
+    },
+  },
+  parallelogram: {
+    name: '平行四边形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(30% 0%, 100% 0%, 70% 100%, 0% 100%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.3} 0 L ${width} 0 L ${width * 0.7} ${height} L 0 ${height} Z`
+    },
+  },
+  parallelogram2: {
+    name: '平行四边形2',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(30% 100%, 100% 100%, 70% 0%, 0% 0%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.3} ${height} L ${width} ${height} L ${width * 0.7} 0 L 0 0 Z`
+    },
+  },
+  trapezoid: {
+    name: '梯形',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(25% 0%, 75% 0%, 100% 100%, 0% 100%)',
+    createPath: (width: number, height: number) => {
+      return `M ${width * 0.25} 0 L ${width * 0.75} 0 L ${width} ${height} L 0 ${height} Z`
+    },
+  },
+  trapezoid2: {
+    name: '梯形2',
+    type: ClipPathTypes.POLYGON,
+    style: 'polygon(0% 0%, 100% 0%, 75% 100%, 25% 100%)',
+    createPath: (width: number, height: number) => {
+      return `M 0 0 L ${width} 0 L ${width * 0.75} ${height} L ${width * 0.25} ${height} Z`
+    },
+  },
+}

+ 274 - 0
src/configs/latex.ts

@@ -0,0 +1,274 @@
+export const FORMULA_LIST = [
+  {
+    label: '高斯公式',
+    latex: `\\int\\int\\int _ { \\Omega } \\left( \\frac { \\partial {P} } { \\partial {x} } + \\frac { \\partial {Q} } { \\partial {y} } + \\frac { \\partial {R} }{ \\partial {z} } \\right) \\mathrm { d } V = \\oint _ { \\partial \\Omega } ( P \\cos \\alpha + Q \\cos \\beta + R \\cos \\gamma ) \\mathrm{ d} S`
+  },
+  {
+    label: '傅里叶级数',
+    latex: `f(x) = \\frac {a_0} 2 + \\sum_{n = 1}^\\infty {({a_n}\\cos {nx} + {b_n}\\sin {nx})}`,
+  },
+  {
+    label: '泰勒展开式',
+    latex: `e ^ { x } = 1 + \\frac { x } { 1 ! } + \\frac { x ^ { 2 } } { 2 ! } + \\frac { x ^ { 3 } } { 3 ! } + ... , \\quad - \\infty < x < \\infty`,
+  },
+  {
+    label: '定积分',
+    latex: `\\lim_ { n \\rightarrow + \\infty } \\sum _ { i = 1 } ^ { n } f \\left[ a + \\frac { i } { n } ( b - a ) \\right] \\frac { b - a } { n } = \\int _ { a } ^ { b } f ( x ) dx`,
+  },
+  {
+    label: '三角恒等式1',
+    latex: `\\sin \\alpha \\pm \\sin \\beta = 2 \\sin \\frac { 1 } { 2 } ( \\alpha \\pm \\beta ) \\cos \\frac { 1 } { 2 } ( \\alpha \\mp \\beta )`,
+  },
+  {
+    label: '三角恒等式2',
+    latex: `\\cos \\alpha + \\cos \\beta = 2 \\cos \\frac { 1 } { 2 } ( \\alpha + \\beta ) \\cos \\frac { 1 } { 2 } ( \\alpha - \\beta )`,
+  },
+  {
+    label: '和的展开式',
+    latex: `( 1 + x ) ^ { n } = 1 + \\frac { n x } { 1 ! } + \\frac { n ( n - 1 ) x ^ { 2 } } { 2 ! } + ...`,
+  },
+  {
+    label: '欧拉公式',
+    latex: ` e^{ix} = \\cos {x} + i\\sin {x}`,
+  },
+  {
+    label: '贝努利方程',
+    latex: `\\frac {dy} {dx} + P(x)y = Q(x) y^n ({n} \\not= {0,1})`,
+  },
+  {
+    label: '全微分方程',
+    latex: `du(x,y) = P(x,y)dx + Q(x,y)dy = 0`,
+  },
+  {
+    label: '非齐次方程',
+    latex: `y = (\\int Q(x) e^{\\int {P(x)dx}}dx + C)e^{-\\int {P(x)dx}}`,
+  },
+  {
+    label: '柯西中值定理',
+    latex: `\\frac{{f(b) - f(a)}}{{F(b) - F(a)}} = \\frac{{f'(\\xi )}}{{F'(\\xi )}}`,
+  },
+  {
+    label: '拉格朗日中值定理',
+    latex: `f(b) - f(a) = f'(\\xi )(b - a)`,
+  },
+  {
+    label: '导数公式',
+    latex: `(\\arcsin x)' = \\frac{1}{{\\sqrt {1 - x^2} }}`,
+  },
+  {
+    label: '三角函数积分',
+    latex: `\\int {tgxdx = - \\ln \\left| {\\cos x} \\right| + C}`,
+  },
+  {
+    label: '二次曲面',
+    latex: `\\frac{{{x^2}}}{{{a^2}}} + \\frac{{{y^2}}}{{{b^2}}} - \\frac{{{z^2}}}{{{c^2}}} = 1`,
+  },
+  {
+    label: '二阶微分',
+    latex: `\\frac {{d^2}y} {dx^2} + P(x) \\frac {dy} {dx} + Q(x)y = f(x)`,
+  },
+  {
+    label: '方向导数',
+    latex: `\\frac{{\\partial f}}{{\\partial l}} = \\frac{{\\partial f}}{{\\partial x}}\\cos \\phi + \\frac{{\\partial f}}{{\\partial y}}\\sin \\phi`,
+  },
+]
+
+export const SYMBOL_LIST = [
+  {
+    type: 'operators',
+    label: '数学',
+    children: [
+      { latex: '\\cdot' },
+      { latex: '\\pm' },
+      { latex: '\\mp' },
+      { latex: '+' },
+      { latex: '-' },
+      { latex: '\\times' },
+      { latex: '\\div' },
+      { latex: '<' },
+      { latex: '>' },
+      { latex: '=' },
+      { latex: '\\neq\\ne' },
+      { latex: '\\leqq' },
+      { latex: '\\geqq' },
+      { latex: '\\leq' },
+      { latex: '\\geq' },
+      { latex: '\\propto' },
+      { latex: '\\sim' },
+      { latex: '\\equiv' },
+      { latex: '\\dagger' },
+      { latex: '\\ddagger' },
+      { latex: '\\ell' },
+      { latex: '\\#' },
+      { latex: '\\$' },
+      { latex: '\\&' },
+      { latex: '\\%' },
+      { latex: '\\langle\\rangle' },
+      { latex: '()' },
+      { latex: '[]' },
+      { latex: '\\{\\}' },
+      { latex: '||' },
+      { latex: '\\|' },
+      { latex: '\\exists' },
+      { latex: '\\in' },
+      { latex: '\\subset' },
+      { latex: '\\supset' },
+      { latex: '\\cup' },
+      { latex: '\\cap' },
+      { latex: '\\infty' },
+      { latex: '\\partial' },
+      { latex: '\\nabla' },
+      { latex: '\\aleph' },
+      { latex: '\\wp' },
+      { latex: '\\therefore' },
+      { latex: '\\mid' },
+      { latex: '\\sum' },
+      { latex: '\\prod' },
+      { latex: '\\bigoplus' },
+      { latex: '\\bigodot' },
+      { latex: '\\int' },
+      { latex: '\\oint' },
+      { latex: '\\oplus' },
+      { latex: '\\odot' },
+      { latex: '\\perp' },
+      { latex: '\\angle' },
+      { latex: '\\triangle' },
+      { latex: '\\Box' },
+      { latex: '\\rightarrow' },
+      { latex: '\\to' },
+      { latex: '\\leftarrow' },
+      { latex: '\\gets' },
+      { latex: '\\circ' },
+      { latex: '\\bigcirc' },
+      { latex: '\\bullet' },
+      { latex: '\\star' },
+      { latex: '\\diamond' },
+      { latex: '\\ast' },
+      { latex: ',' },
+      { latex: '.' },
+      { latex: ';' },
+      { latex: '!' },
+    ],
+  },
+  {
+    type: 'group',
+    label: '组合',
+    children: [
+      { latex: '\\frac{a}{b}' },
+      { latex: '\\frac{dx}{dx}' },
+      { latex: '\\frac{\\partial a}{\\partial b}' },
+      { latex: '\\sqrt{x}' },
+      { latex: '\\sqrt[n]{x}' },
+      { latex: 'x^{n}' },
+      { latex: 'x_{n}' },
+      { latex: 'x_a^b' },
+      { latex: '\\int_{a}^{b}' },
+      { latex: '\\oint_a^b' },
+      { latex: '\\lim_{a \\rightarrow b}' },
+      { latex: '\\prod_a^b' },
+      { latex: '\\sum_a^b' },
+      { latex: '\\left(\\begin{array}a \\\\ b\\end{array}\\right)' },
+      { latex: '\\begin{bmatrix}a & b \\\\ c & d \\end{bmatrix}' },
+      { latex: '\\begin{cases}a & x = 0 \\\\ b & x > 0\\end{cases}' },
+      { latex: '\\hat{a}' },
+      { latex: '\\breve{a}' },
+      { latex: '\\acute{a}' },
+      { latex: '\\grave{a}' },
+      { latex: '\\tilde{a}' },
+      { latex: '\\bar{a}' },
+      { latex: '\\vec{a}' },
+      { latex: '\\underline{a}' },
+      { latex: '\\overline{a}' },
+      { latex: '\\widehat{ab}' },
+      { latex: '\\overleftarrow{ab}' },
+      { latex: '\\overrightarrow{ab}' },
+    ],
+  },
+  {
+    type: 'verbatim',
+    label: '函数',
+    children: [
+      { latex: '\\log' },
+      { latex: '\\ln' },
+      { latex: '\\exp' },
+      { latex: '\\mod' },
+      { latex: '\\lim' },
+      { latex: '\\sin' },
+      { latex: '\\cos' },
+      { latex: '\\tan' },
+      { latex: '\\csc' },
+      { latex: '\\sec' },
+      { latex: '\\cot' },
+      { latex: '\\sinh' },
+      { latex: '\\cosh' },
+      { latex: '\\tanh' },
+      { latex: '\\csch' },
+      { latex: '\\sech' },
+      { latex: '\\coth' },
+      { latex: '\\arcsin' },
+      { latex: '\\arccos' },
+      { latex: '\\arctan' },
+      { latex: '\\arccsc' },
+      { latex: '\\arcsec' },
+      { latex: '\\arccot' },
+    ],
+  },
+  {
+    type: 'greek',
+    label: '希腊字母',
+    children: [
+      { latex: '\\alpha' },
+      { latex: '\\beta' },
+      { latex: '\\gamma' },
+      { latex: '\\delta' },
+      { latex: '\\varepsilon' },
+      { latex: '\\zeta' },
+      { latex: '\\eta' },
+      { latex: '\\vartheta' },
+      { latex: '\\iota' },
+      { latex: '\\kappa' },
+      { latex: '\\lambda' },
+      { latex: '\\mu' },
+      { latex: '\\nu' },
+      { latex: '\\xi' },
+      { latex: '\\omicron' },
+      { latex: '\\pi' },
+      { latex: '\\rho' },
+      { latex: '\\sigma' },
+      { latex: '\\tau' },
+      { latex: '\\upsilon' },
+      { latex: '\\varphi' },
+      { latex: '\\chi' },
+      { latex: '\\psi' },
+      { latex: '\\omega' },
+      { latex: '\\epsilon' },
+      { latex: '\\theta' },
+      { latex: '\\phi' },
+      { latex: '\\varsigma' },
+      { latex: '\\Alpha' },
+      { latex: '\\Beta' },
+      { latex: '\\Gamma' },
+      { latex: '\\Delta' },
+      { latex: '\\Epsilon' },
+      { latex: '\\Zeta' },
+      { latex: '\\Eta' },
+      { latex: '\\Theta' },
+      { latex: '\\Iota' },
+      { latex: '\\Kappa' },
+      { latex: '\\Lambda' },
+      { latex: '\\Mu' },
+      { latex: '\\Nu' },
+      { latex: '\\Xi' },
+      { latex: '\\Omicron' },
+      { latex: '\\Pi' },
+      { latex: '\\Rho' },
+      { latex: '\\Sigma' },
+      { latex: '\\Tau' },
+      { latex: '\\Upsilon' },
+      { latex: '\\Phi' },
+      { latex: '\\Chi' },
+      { latex: '\\Psi' },
+      { latex: '\\Omega' },
+    ],
+  },
+]

+ 39 - 0
src/configs/lines.ts

@@ -0,0 +1,39 @@
+import type { LinePoint } from '@/types/slides'
+
+
+export interface LinePoolItem {
+  path: string
+  style: 'solid' | 'dashed'
+  points: [LinePoint, LinePoint]
+  isBroken?: boolean
+  isBroken2?: boolean
+  isCurve?: boolean
+  isCubic?: boolean
+}
+
+interface PresetLine {
+  type: string
+  children: LinePoolItem[]
+}
+
+export const LINE_LIST: PresetLine[] = [
+  {
+    type: '直线',
+    children: [
+      { path: 'M 0 0 L 20 20', style: 'solid', points: ['', ''] },
+      { path: 'M 0 0 L 20 20', style: 'dashed', points: ['', ''] },
+      { path: 'M 0 0 L 20 20', style: 'solid', points: ['', 'arrow'] },
+      { path: 'M 0 0 L 20 20', style: 'dashed', points: ['', 'arrow'] },
+      { path: 'M 0 0 L 20 20', style: 'solid', points: ['', 'dot'] },
+    ],
+  },
+  {
+    type: '折线、曲线',
+    children: [
+      { path: 'M 0 0 L 0 20 L 20 20', style: 'solid', points: ['', 'arrow'], isBroken: true },
+      { path: 'M 0 0 L 10 0 L 10 20 L 20 20', style: 'solid', points: ['', 'arrow'], isBroken2: true },
+      { path: 'M 0 0 Q 0 20 20 20', style: 'solid', points: ['', 'arrow'], isCurve: true },
+      { path: 'M 0 0 C 20 0 0 20 20 20', style: 'solid', points: ['', 'arrow'], isCubic: true },
+    ],
+  },
+]

+ 1038 - 0
src/configs/shapes.ts

@@ -0,0 +1,1038 @@
+/* eslint-disable max-lines */
+
+// 非专业设计人士可以用该应用绘制基本形状:https://github.com/pipipi-pikachu/svgPathCreator
+
+import { ShapePathFormulasKeys } from '@/types/slides'
+
+export interface ShapePoolItem {
+  viewBox: [number, number]
+  path: string
+  special?: boolean
+  pathFormula?: ShapePathFormulasKeys
+  outlined?: boolean
+  pptxShapeType?: string
+  title?: string
+  withborder?: boolean
+}
+
+interface ShapeListItem {
+  type: string
+  children: ShapePoolItem[]
+}
+
+export interface ShapePathFormula {
+  editable?: boolean
+  defaultValue?: number[]
+  range?: [number, number][]
+  relative?: string[]
+  getBaseSize?: ((width: number, height: number) => number)[]
+  formula: (width: number, height: number, values?: number[]) => string
+}
+
+export const SHAPE_PATH_FORMULAS: {
+  [key: string]: ShapePathFormula
+} = {
+  [ShapePathFormulasKeys.ROUND_RECT]: {
+    editable: true,
+    defaultValue: [0.125],
+    range: [[0, 0.5]],
+    relative: ['left'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height - radius} Q ${width} ${height} ${width - radius} ${height} L ${radius} ${height} Q 0 ${height} 0 ${height - radius} L 0 ${radius} Q 0 0 ${radius} 0 Z`
+    }
+  },
+  [ShapePathFormulasKeys.CUT_RECT_DIAGONAL]: {
+    editable: true,
+    defaultValue: [0.2],
+    range: [[0, 0.9]],
+    relative: ['right'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M 0 ${height - radius} L 0 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L ${radius} ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.CUT_RECT_SINGLE]: {
+    editable: true,
+    defaultValue: [0.2],
+    range: [[0, 0.9]],
+    relative: ['right'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M 0 ${height} L 0 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.CUT_RECT_SAMESIDE]: {
+    editable: true,
+    defaultValue: [0.2],
+    range: [[0, 0.5]],
+    relative: ['left'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M 0 ${radius} L ${radius} 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L 0 ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.ROUND_RECT_DIAGONAL]: {
+    editable: true,
+    defaultValue: [0.125],
+    range: [[0, 1]],
+    relative: ['left'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M ${radius} 0 L ${width} 0 L ${width} ${height - radius} Q ${width} ${height} ${width - radius} ${height} L 0 ${height} L 0 ${radius} Q 0 0 ${radius} 0 Z`
+    }
+  },
+  [ShapePathFormulasKeys.ROUND_RECT_SINGLE]: {
+    editable: true,
+    defaultValue: [0.125],
+    range: [[0, 1]],
+    relative: ['right'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M 0 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height} L 0 ${height} L 0 0 Z`
+    }
+  },
+  [ShapePathFormulasKeys.ROUND_RECT_SAMESIDE]: {
+    editable: true,
+    defaultValue: [0.125],
+    range: [[0, 0.5]],
+    relative: ['left'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M 0 ${radius} Q 0 0 ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height} L 0 ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.CUT_ROUND_RECT]: {
+    editable: true,
+    defaultValue: [0.125],
+    range: [[0, 0.5]],
+    relative: ['left'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const radius = Math.min(width, height) * values![0]
+      return `M ${radius} 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L 0 ${height} L 0 ${radius} Q 0 0 ${radius} 0 Z`
+    }
+  },
+  [ShapePathFormulasKeys.MESSAGE]: {
+    editable: true,
+    range: [[0, 0.8], [0.1, 0.3]],
+    defaultValue: [0.3, 0.2],
+    relative: ['left_bottom', 'bottom'],
+    getBaseSize: [
+      width => width,
+      (width, height) => height,
+    ],
+    formula: (width, height, values) => {
+      const point = width * values![0]
+      const arrowWidth = width * 0.2
+      const arrowheight = height * values![1]
+      return `M 0 0 L ${width} 0 L ${width} ${height - arrowheight} L ${point + arrowWidth} ${height - arrowheight} L ${point} ${height} L ${point} ${height - arrowheight} L 0 ${height - arrowheight} Z`
+    }
+  },
+  [ShapePathFormulasKeys.ROUND_MESSAGE]: {
+    formula: (width, height) => {
+      const radius = Math.min(width, height) * 0.125
+      const arrowWidth = Math.min(width, height) * 0.2
+      const arrowheight = Math.min(width, height) * 0.2
+      return `M 0 ${radius} Q 0 0 ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height - radius - arrowheight} Q ${width} ${height - arrowheight} ${width - radius} ${height - arrowheight} L ${width / 2} ${height - arrowheight} L ${width / 2 - arrowWidth} ${height} L ${width / 2 - arrowWidth} ${height - arrowheight} L ${radius} ${height - arrowheight} Q 0 ${height - arrowheight} 0 ${height - radius - arrowheight} L 0 ${radius} Z`
+    }
+  },
+  [ShapePathFormulasKeys.L]: {
+    editable: true,
+    defaultValue: [0.25],
+    range: [[0.1, 0.9]],
+    relative: ['left'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const lineWidth = Math.min(width, height) * values![0]
+      return `M 0 0 L 0 ${height} L ${width} ${height} L ${width} ${height - lineWidth} L ${lineWidth} ${height - lineWidth} L ${lineWidth} 0 Z`
+    }
+  },
+  [ShapePathFormulasKeys.RING_RECT]: {
+    editable: true,
+    defaultValue: [0.25],
+    range: [[0.1, 0.45]],
+    relative: ['left'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const lineWidth = Math.min(width, height) * values![0]
+      return `M 0 0 ${width} 0 ${width} ${height} L 0 ${height} L 0 0 Z M ${lineWidth} ${lineWidth} L ${lineWidth} ${height - lineWidth} L ${width - lineWidth} ${height - lineWidth} L ${width - lineWidth} ${lineWidth} Z`
+    }
+  },
+  [ShapePathFormulasKeys.PLUS]: {
+    editable: true,
+    defaultValue: [0.25],
+    range: [[0.1, 0.9]],
+    relative: ['center'],
+    getBaseSize: [(width, height) => Math.min(width, height)],
+    formula: (width, height, values) => {
+      const lineWidth = Math.min(width, height) * values![0]
+      return `M ${width / 2 - lineWidth / 2} 0 L ${width / 2 - lineWidth / 2} ${height / 2 - lineWidth / 2} L 0 ${height / 2 - lineWidth / 2} L 0 ${height / 2 + lineWidth / 2} L ${width / 2 - lineWidth / 2} ${height / 2 + lineWidth / 2} L ${width / 2 - lineWidth / 2} ${height} L ${width / 2 + lineWidth / 2} ${height} L ${width / 2 + lineWidth / 2} ${height / 2 + lineWidth / 2} L ${width} ${height / 2 + lineWidth / 2} L ${width} ${height / 2 - lineWidth / 2} L ${width / 2 + lineWidth / 2} ${height / 2 - lineWidth / 2} L ${width / 2 + lineWidth / 2} 0 Z`
+    }
+  },
+  [ShapePathFormulasKeys.TRIANGLE]: {
+    editable: true,
+    defaultValue: [0.5],
+    range: [[0, 1]],
+    relative: ['left'],
+    getBaseSize: [width => width],
+    formula: (width, height, values) => {
+      const vertex = width * values![0]
+      return `M ${vertex} 0 L 0 ${height} L ${width} ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.PARALLELOGRAM_LEFT]: {
+    editable: true,
+    defaultValue: [0.25],
+    range: [[0, 0.9]],
+    relative: ['left'],
+    getBaseSize: [width => width],
+    formula: (width, height, values) => {
+      const point = width * values![0]
+      return `M ${point} 0 L ${width} 0 L ${width - point} ${height} L 0 ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.PARALLELOGRAM_RIGHT]: {
+    editable: true,
+    defaultValue: [0.25],
+    range: [[0, 0.9]],
+    relative: ['right'],
+    getBaseSize: [width => width],
+    formula: (width, height, values) => {
+      const point = width * values![0]
+      return `M 0 0 L ${width - point} 0 L ${width} ${height} L ${point} ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.TRAPEZOID]: {
+    editable: true,
+    defaultValue: [0.25],
+    range: [[0, 0.5]],
+    relative: ['left'],
+    getBaseSize: [width => width],
+    formula: (width, height, values) => {
+      const point = width * values![0]
+      return `M ${point} 0 L ${width - point} 0 L ${width} ${height} L 0 ${height} Z`
+    }
+  },
+  [ShapePathFormulasKeys.BULLET]: {
+    editable: true,
+    defaultValue: [0.2],
+    range: [[0, 1]],
+    relative: ['top'],
+    getBaseSize: [(width, height) => height],
+    formula: (width, height, values) => {
+      const point = height * values![0]
+      return `M ${width / 2} 0 L 0 ${point} L 0 ${height} L ${width} ${height} L ${width} ${point} Z`
+    }
+  },
+  [ShapePathFormulasKeys.INDICATOR]: {
+    editable: true,
+    defaultValue: [0.2],
+    range: [[0, 0.9]],
+    relative: ['right'],
+    getBaseSize: [width => width],
+    formula: (width, height, values) => {
+      const point = width * values![0]
+      return `M ${width} ${height / 2} L ${width - point} 0 L 0 0 L ${point} ${height / 2} L 0 ${height} L ${width - point} ${height} Z`
+    }
+  },
+}
+
+export const SHAPE_LIST: ShapeListItem[] = [
+  {
+    type: '矩形',
+    children: [
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
+        pptxShapeType: 'rect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 50 0 L 150 0 Q 200 0 200 50 L 200 150 Q 200 200 150 200 L 50 200 Q 0 200 0 150 L 0 50 Q 0 0 50 0 Z',
+        pathFormula: ShapePathFormulasKeys.ROUND_RECT,
+        pptxShapeType: 'roundRect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 200 L 0 0 L 150 0 L 200 50 L 200 200 Z',
+        pathFormula: ShapePathFormulasKeys.CUT_RECT_SINGLE,
+        pptxShapeType: 'snip1Rect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 50 L 50 0 L 150 0 L 200 50 L 200 200 L 0 200 Z',
+        pathFormula: ShapePathFormulasKeys.CUT_RECT_SAMESIDE,
+        pptxShapeType: 'snip2SameRect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 150 L 0 0 L 150 0 L 200 50 L 200 200 L 50 200 Z',
+        pathFormula: ShapePathFormulasKeys.CUT_RECT_DIAGONAL,
+        pptxShapeType: 'snip2DiagRect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 50 0 L 150 0 L 200 50 L 200 200 L 0 200 L 0 50 Q 0 0 50 0 Z',
+        pathFormula: ShapePathFormulasKeys.CUT_ROUND_RECT,
+        pptxShapeType: 'snipRoundRect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 150 0 Q 200 0 200 50 L 200 200 L 0 200 L 0 0 Z',
+        pathFormula: ShapePathFormulasKeys.ROUND_RECT_SINGLE,
+        pptxShapeType: 'round1Rect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 50 Q 0 0 50 0 L 150 0 Q 200 0 200 50 L 200 200 L 0 200 Z',
+        pathFormula: ShapePathFormulasKeys.ROUND_RECT_SAMESIDE,
+        pptxShapeType: 'round2SameRect',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 50 0 L 200 0 L 200 150 Q 200 200 150 200 L 0 200 L 0 50 Q 0 0 50 0 Z',
+        pathFormula: ShapePathFormulasKeys.ROUND_RECT_DIAGONAL,
+        pptxShapeType: 'round2DiagRect',
+      },
+    ]
+  },
+
+  {
+    type: '常用形状',
+    children: [
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z',
+        pptxShapeType: 'ellipse',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 0 200 L 200 200 L 100 0 Z',
+        pathFormula: ShapePathFormulasKeys.TRIANGLE,
+        pptxShapeType: 'triangle',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 0 200 L 200 200 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 70 20 L 0 160 Q 0 200 40 200 L 160 200 Q 200 200 200 160 L 130 20 Q 100 -20 70 20 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 50 0 L 200 0 L 150 200 L 0 200 L 50 0 Z',
+        pathFormula: ShapePathFormulasKeys.PARALLELOGRAM_LEFT,
+        pptxShapeType: 'parallelogram',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 150 0 L 200 200 L 50 200 L 0 0 Z',
+        pathFormula: ShapePathFormulasKeys.PARALLELOGRAM_RIGHT,
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 50 0 L 150 0 L 200 200 L 0 200 L 50 0 Z',
+        pathFormula: ShapePathFormulasKeys.TRAPEZOID,
+        pptxShapeType: 'trapezoid',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 0 100 L 100 200 L 200 100 L 100 0 Z',
+        pptxShapeType: 'diamond',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 0 50 L 0 200 L 200 200 L 200 50 L 100 0 Z',
+        pathFormula: ShapePathFormulasKeys.BULLET,
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 100 L 150 0 L 0 0 L 50 100 L 0 200 L 150 200 L 200 100 Z',
+        pathFormula: ShapePathFormulasKeys.INDICATOR,
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 C 80 20 120 20 200 0 C 180 80 180 120 200 200 C 80 180 120 180 0 200 C 20 120 20 80 0 0 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 10 10 C 60 0 140 0 190 10 C 200 60 200 140 190 190 C 140 200 60 200 10 190 C 0 140 0 60 10 10 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 200 A 50 100 0 1 1 200 200 L 0 200 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 40 20 A 100 100 0 1 0 200 100 L 100 100 L 40 20 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 100 100 102 1 0 200 100 L 100 100 L 100 0 Z',
+        pptxShapeType: 'pie',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 160 20 A 100 100 0 1 0 200 100 L 100 100 L 160 20 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 100 100 102 1 0 200 100 L 100 0 Z',
+        pptxShapeType: 'chord',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 100 100 102 1 0 200 100 L 200 0 L 100 0 Z',
+        pptxShapeType: 'teardrop',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 200 0 Q 200 200 0 200 L 0 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: `M100,0 L200,76.6 L161.8,200 L38.2,200 L0,76.6 Z`,
+        pptxShapeType: 'pentagon',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 40 0 L 160 0 L 200 100 L 160 200 L 40 200 L 0 100 Z',
+        pptxShapeType: 'hexagon',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 0 60 L 0 140 L 100 200 L 200 140 L 200 60 L 100 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: `M100,0 L170.71,29.29 L200,100 L170.71,170.71 L100,200 L29.29,170.71 L0,100 L29.29,29.29 Z`,
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 60 0 L 140 0 L 200 60 L 200 140 L 140 200 L 60 200 L 0 140 L 0 60 L 60 0 Z',
+        pptxShapeType: 'octagon',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 75 0 L 125 0 L 175 25 L 200 75 L 200 125 L 175 175 L 125 200 L 75 200 L 25 175 L 0 125 L 0 75 L 25 25 L 75 0 Z',
+        pptxShapeType: 'dodecagon',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 150 0 A 50 100 0 1 1 150 200 L 0 200 L 0 0 L 150 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 50 0 A 25 50 0 1 0 50 200 L 150 200 A 25 50 0 1 0 150 0 L 50 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 150 0 A 50 100 0 1 1 150 200 L 0 200 A 50 100 0 0 0 0 0 L 150 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 0 L 200 200 L 0 200 L 0 100 L 200 0 Z',
+        pptxShapeType: 'flowChartManualInput',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 200 100 L 200 200 L 0 200 L 0 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 200 0 L 200 150 C 110 140 110 240 0 180 Z',
+        pptxShapeType: 'flowChartDocument',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 0 L 100 0 L 0 100 L 0 200 L 200 0 Z',
+        pptxShapeType: 'diagStripe',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 50 0 L 150 0 L 150 50 L 200 50 L 200 150 L 150 150 L 150 200 L 50 200 L 50 150 L 0 150 L 0 50 L 50 50 L 50 0 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 0 200 L 200 200 L 200 140 L 60 140 L 60 0 L 0 0 Z',
+        pathFormula: ShapePathFormulasKeys.L,
+        pptxShapeType: 'corner',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M0 0 L200 0 L200 200 L0 200 L0 0 Z M50 50 L50 150 L150 150 L150 50 Z',
+        pathFormula: ShapePathFormulasKeys.RING_RECT,
+        pptxShapeType: 'frame',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M0 100 A100 100 0 1 1 0 101 Z M150 100 A50 50 0 1 0 150 101 Z',
+        pptxShapeType: 'donut',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 70 0 L 70 70 L 0 70 L 0 130 L 70 130 L 70 200 L 130 200 L 130 130 L 200 130 L 200 70 L 130 70 L 130 0 L 70 0 Z',
+        pathFormula: ShapePathFormulasKeys.PLUS,
+        pptxShapeType: 'mathPlus',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 70 L 200 70 L 200 130 L 0 130 Z',
+        pptxShapeType: 'mathMinus',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 40 0 L 0 40 L 60 100 L 0 160 L 40 200 L 100 140 L 160 200 L 200 160 L 140 100 L 200 40 L 160 0 L 100 60 L 40 0 Z',
+        pptxShapeType: 'mathMultiply',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 80 L 200 80 L 200 120 L 0 120 Z M 100 0 A 25 25 0 1 1 100 50 A 25 25 0 1 1 100 0 M 100 200 A 25 25 0 1 1 100 150 A 25 25 0 1 1 100 200',
+        pptxShapeType: 'mathDivide',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 30 L 200 30 L 200 80 L 0 80 Z M 0 120 L 200 120 L 200 170 L 0 170 Z',
+        pptxShapeType: 'mathEqual',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 120 0 L 170 0 L 150 40 L 200 40 L 200 80 L 130 80 L 110 120 L 200 120 L 200 160 L 90 160 L 70 200 L 20 200 L 40 160 L 0 160 L 0 120 L 60 120 L 80 80 L 0 80 L 0 40 L 100 40 Z',
+        pptxShapeType: 'mathNotEqual',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 200 0 L 200 160 L 100 160 L 60 200 L 60 160 L 0 160 Z',
+        pathFormula: ShapePathFormulasKeys.MESSAGE,
+        pptxShapeType: 'wedgeRectCallout',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 40 Q 0 0 40 0 L 160 0 Q 200 0 200 40 L 200 120 Q 200 160 160 160 L 100 160 L 60 200 L 60 160 L 40 160 Q 0 160 0 120 L 0 40 Z',
+        pathFormula: ShapePathFormulasKeys.ROUND_MESSAGE,
+        pptxShapeType: 'wedgeRoundRectCallout',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 180 160 A 100 100 0 1 0 100 200 L 200 200 L 200 160 L 180 160 Z',
+        pptxShapeType: 'flowChartMagneticTape',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 0 L 0 0 L 200 200 L 0 200 L 200 0 Z',
+        pptxShapeType: 'flowChartCollate',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 20 C 60 60 140 -40 200 20 L 200 180 C 140 140 60 240 0 180 L 0 20 Z',
+        pptxShapeType: 'wave',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 20 C 40 -40 60 60 100 20 C 140 -40 160 60 200 20 L 200 180 C 140 240 160 140 100 180 C 40 240 60 140 0 180 L 0 20 Z',
+        pptxShapeType: 'doubleWave',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 Q 0 50 0 175 Q 100 225 200 175 Q 200 50 100 0 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 100 A 50 50 0 1 1 200 100 L 100 200 L 0 100 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 120 80 L 200 100 L 120 120 L 100 200 L 80 120 L 0 100 L 80 80 L 100 0 Z',
+        pptxShapeType: 'star4',
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1018.67652554 400.05983681l-382.95318779-5.89158658L512 34.78141155 388.27666225 394.16825023l-382.95318779 5.89158658L311.68602415 629.83174977l-117.83174978 365.27842665 312.25413766-223.88032637 312.25413904 223.88032637-117.83175116-365.27842665 318.14572563-229.77191296z',
+        pptxShapeType: 'star5',
+        special: true,
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 60 60 L 0 100 L 60 140 L 100 200 L 140 140 L 200 100 L 140 60 L 100 0 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 140 60 L 200 60 L 160 100 L 200 140 L 140 140 L 100 200 L 60 140 L 0 140 L 40 100 L 0 60 L 60 60 L 100 0 Z',
+        pptxShapeType: 'star6',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 130 30 L 170 30 L 170 70 L 200 100 L 170 130 L 170 170 L 130 170 L 100 200 L 70 170 L 30 170 L 30 130 L 0 100 L 30 70 L 30 30 L 70 30 L 100 0',
+        pptxShapeType: 'star8',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 50 50 0 1 0 200 120 A 100 100 0 1 1 100 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 120 0 L 100 80 L 200 80 L 80 200 L 100 120 L 0 120 L 120 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 30 50 Q 40 -20 120 10 Q 180 -10 180 40 Q 210 70 190 100 C 210 140 180 170 160 170 Q 140 210 100 180 C 70 210 20 190 30 150 C -10 140 -10 80 30 50',
+        pptxShapeType: 'cloud',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 0 100 L 100 200 L 200 100 L 100 0 Z M 200 100 L 0 100',
+        withborder: true,
+        pptxShapeType: 'flowChartSort',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z M 170 30 L 30 170',
+        withborder: true,
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z M 30 30 L 170 170',
+        withborder: true,
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z M 170 30 L 30 170 M 30 30 L 170 170',
+        withborder: true,
+        pptxShapeType: 'flowChartSummingJunction',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z M 200 100 L 0 100 M 100 0 L 100 200',
+        withborder: true,
+        pptxShapeType: 'flowChartOr',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 160 0 A 40 100 0 1 1 160 200 L 40 200 A 40 100 0 1 1 40 0 L 160 0 Z M 160 200 A 40 100 0 1 1 160 0',
+        withborder: true,
+        pptxShapeType: 'flowChartMagneticDrum',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 40 A 50 20 0 1 1 200 40 L 200 160 A 50 20 0 1 1 0 160 L 0 40 Z M 200 40 A 50 20 0 1 1 0 40',
+        withborder: true,
+        pptxShapeType: 'can',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 0 L 50 0 L 0 50 L 0 200 L 150 200 L 200 150 L 200 0 Z M 200 0 L 150 50 M 150 50 L 0 50 M 150 50 L 150 200',
+        withborder: true,
+        pptxShapeType: 'cube',
+      },
+    ],
+  },
+  
+  {
+    type: '箭头',
+    children: [
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 0 100 L 50 100 L 50 200 L 150 200 L 150 100 L 200 100 L 100 0 Z',
+        pptxShapeType: 'upArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 200 L 200 100 L 150 100 L 150 0 L 50 0 L 50 100 L 0 100 L 100 200 Z',
+        pptxShapeType: 'downArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 100 L 100 0 L 100 50 L 200 50 L 200 150 L 100 150 L 100 200 L 0 100 Z',
+        pptxShapeType: 'leftArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 100 L 100 0 L 100 50 L 0 50 L 0 150 L 100 150 L 100 200 L 200 100 Z',
+        pptxShapeType: 'rightArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 0 60 L 60 60 L 60 140 L 0 140 L 100 200 L 200 140 L 140 140 L 140 60 L 200 60 L 100 0 Z',
+        pptxShapeType: 'upDownArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 100 L 60 0 L 60 60 L 140 60 L 140 0 L 200 100 L 140 200 L 140 140 L 60 140 L 60 200 L 0 100 Z',
+        pptxShapeType: 'leftRightArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 100 0 L 60 40 L 80 40 L 80 80 L 40 80 L 40 60 L 0 100 L 40 140 L 40 120 L 80 120 L 80 160 L 60 160 L 100 200 L 140 160 L 120 160 L 120 120 L 160 120 L 160 140 L 200 100 L 160 60 L 160 80 L 120 80 L 120 40 L 140 40 L 100 0 Z',
+        pptxShapeType: 'quadArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 100 L 100 0 L 100 50 L 200 50 L 150 100 L 200 150 L 100 150 L 100 200 L 0 100 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 100 L 100 0 L 100 50 L 0 50 L 50 100 L 0 150 L 100 150 L 100 200 L 200 100 Z',
+        pptxShapeType: 'notchedRightArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 100 L 80 20 L 80 80 L 120 80 L 120 0 L 200 0 L 200 200 L 120 200 L 120 120 L 80 120 L 80 180 L 0 100 Z',
+        pptxShapeType: 'leftArrowCallout',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 100 L 120 20 L 120 80 L 80 80 L 80 0 L 0 0 L 0 200 L 80 200 L 80 120 L 120 120 L 120 180 L 200 100 Z',
+        pptxShapeType: 'rightArrowCallout',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 120 0 L 200 100 L 120 200 L 0 200 L 80 100 L 0 0 Z',
+        pptxShapeType: 'chevron',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 80 0 L 200 0 L 120 100 L 200 200 L 80 200 L 0 100 L 80 0 Z',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 140 0 L 200 100 L 140 200 L 0 200 L 0 100 L 0 0 Z',
+        pptxShapeType: 'homePlate',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 60 0 L 200 0 L 200 100 L 200 200 L 60 200 L 0 100 L 60 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 200 100 L 0 200 L 60 100 L 0 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 0 L 0 100 L 200 200 L 140 100 L 200 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 0 L 80 0 L 200 100 L 80 200 L 0 200 L 120 100 L 0 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 200 0 L 120 0 L 0 100 L 120 200 L 200 200 L 80 100 L 200 0 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 200 L 180 200 L 180 40 L 200 40 L 160 0 L 120 40 L 140 40 L 140 160 L 0 160 L 0 200 Z',
+        pptxShapeType: 'bentUpArrow',
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 0 200 L 0 20 L 160 20 L 160 0 L 200 40 L 160 80 L 160 60 L 40 60 L 40 200 L 0 200 Z'
+      },
+      {
+        viewBox: [200, 200],
+        path: 'M 40 180 L 180 180 L 180 40 L 200 40 L 160 0 L 120 40 L 140 40 L 140 140 L 40 140 L 40 120 L 0 160 L 40 200 L 40 180 Z',
+        pptxShapeType: 'leftUpArrow',
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M398.208 302.912V64L0 482.112l398.208 418.176V655.36c284.48 0 483.584 95.552 625.792 304.64-56.896-298.688-227.584-597.312-625.792-657.088z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M625.792 302.912V64L1024 482.112l-398.208 418.176V655.36C341.312 655.36 142.208 750.912 0 960c56.896-298.688 227.584-597.312 625.792-657.088z',
+        special: true,
+      },
+    ],
+  },
+
+  {
+    type: '其他形状',
+    children: [
+      {
+        viewBox: [1024, 1024],
+        path: 'M995.336 243.4016c-15.7584-36.5736-38.3376-69.26639999-66.91440001-97.37280001-28.5768-27.98879999-61.73999999-49.8624-98.78399999-65.26799998-38.22-15.876-78.6744-23.8728-120.4224-23.87280001-57.97680001 0-114.5424 15.876-163.69919999 45.864-11.76 7.17360001-22.932 15.05279999-33.51600001 23.63760001-10.584-8.5848-21.75600001-16.46400001-33.51600001-23.63760001-49.1568-29.98799999-105.7224-45.86399999-163.69919999-45.864-41.74799999 0-82.2024 7.9968-120.4224 23.87280001-36.9264 15.28799999-70.2072 37.27919999-98.78399999 65.26799998-28.6944 28.10640001-51.156 60.79919999-66.91440001 97.37280001-16.34639999 37.9848-24.696 78.3216-24.696 119.83439999 0 39.1608 7.9968 79.96800001 23.8728 121.48080001 13.28880001 34.692 32.34000001 70.67760001 56.6832 107.016 38.57279999 57.5064 91.61040001 117.4824 157.4664 178.28160001 109.1328 100.78319999 217.2072 170.4024 221.79359999 173.22479998l27.87120001 17.8752c12.348 7.8792 28.224 7.8792 40.572 0l27.87119999-17.8752c4.58639999-2.94 112.54319999-72.44159999 221.79360001-173.22479998 65.85599999-60.79919999 118.89359999-120.7752 157.4664-178.28160001 24.3432-36.33839999 43.512-72.324 56.68319999-107.016 15.876-41.5128 23.8728-82.32 23.87280001-121.48080001 0.1176-41.5128-8.232-81.8496-24.5784-119.83439999z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M985.20746667 343.50079998l-303.32586667-44.08319999L546.28693333 24.5248c-3.70346666-7.5264-9.79626667-13.6192-17.32266665-17.32266668-18.87573334-9.3184-41.81333333-1.55306667-51.25120001 17.32266668L342.1184 299.41759999l-303.32586667 44.08319999c-8.36266667 1.19466667-16.00853333 5.13706667-21.8624 11.11040001-14.69440001 15.17226667-14.45546667 39.30453334 0.71679999 54.1184l219.46026668 213.9648-51.84853333 302.1312c-1.43359999 8.24320001-0.11946667 16.8448 3.82293333 24.25173333 9.79626667 18.6368 32.9728 25.92426667 51.6096 16.00853334L512 822.44266665l271.3088 142.64320001c7.40693333 3.9424 16.00853333 5.25653333 24.25173333 3.82293333 20.78719999-3.584 34.7648-23.296 31.1808-44.0832l-51.84853333-302.1312 219.46026668-213.9648c5.97333334-5.85386666 9.91573333-13.49973334 11.11039999-21.8624 3.2256-20.90666667-11.34933333-40.26026667-32.256-43.36640001z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M852.65066667 405.84533333C800.54044445 268.40177778 667.76177778 170.66666667 512.22755555 170.66666667S223.91466667 268.288 171.80444445 405.73155555C74.29688889 431.33155555 2.27555555 520.07822222 2.27555555 625.77777778c0 125.72444445 101.83111111 227.55555555 227.44177778 227.55555555h564.56533334C919.89333333 853.33333333 1021.72444445 751.50222222 1021.72444445 625.77777778c0-105.472-71.79377778-194.21866667-169.07377778-219.93244445z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M926.25224691 323.7371485H654.6457886L898.88200917 15.14388241c5.05486373-6.53433603 0.49315743-16.02761669-7.76722963-16.02761668H418.30008701c-3.45210206 0-6.78091476 1.84934039-8.50696579 4.93157436L90.35039154 555.76772251c-3.82197013 6.53433603 0.86302552 14.7947231 8.50696578 14.79472311h215.01664245l-110.22068713 440.88274851c-2.34249783 9.61657002 9.24670194 16.39748478 16.39748477 9.49328065L933.03316167 340.62779071c6.41104668-6.0411786 2.09591911-16.8906422-6.78091476-16.89064221z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M878.47822222 463.30311111c-22.18666667-49.83466667-53.93066667-93.98044445-94.32177777-131.072l-33.10933334-30.37866666c-4.89244445-4.32355555-12.62933333-2.38933333-14.79111111 3.75466666l-14.79111111 42.43911111c-9.216 26.624-26.16888889 53.81688889-50.176 80.55466667-1.59288889 1.70666667-3.41333333 2.16177778-4.66488889 2.27555556-1.25155555 0.11377778-3.18577778-0.11377778-4.89244445-1.70666667-1.59288889-1.36533333-2.38933333-3.41333333-2.27555555-5.46133333 4.20977778-68.49422222-16.27022222-145.74933333-61.09866667-229.83111112C561.26577778 124.01777778 509.72444445 69.51822222 445.32622222 31.51644445l-46.99022222-27.648c-6.144-3.64088889-13.99466667 1.13777778-13.65333333 8.30577777l2.50311111 54.61333333c1.70666667 37.31911111-2.61688889 70.31466667-12.85688889 97.73511112-12.51555555 33.56444445-30.49244445 64.73955555-53.47555556 92.72888888-16.15644445 19.56977778-34.24711111 37.20533333-54.04444444 52.45155556-47.90044445 36.75022222-87.38133333 84.65066667-114.11911111 138.24C125.72444445 502.10133333 111.50222222 562.74488889 111.50222222 623.50222222c0 53.70311111 10.58133333 105.69955555 31.51644445 154.73777778 20.25244445 47.21777778 49.152 89.77066667 85.90222222 126.17955555 36.864 36.40888889 79.64444445 65.08088889 127.31733333 84.992C405.61777778 1010.11911111 457.95555555 1020.58666667 512 1020.58666667s106.38222222-10.46755555 155.76177778-31.06133334c47.67288889-19.91111111 90.56711111-48.46933333 127.31733333-84.992 36.864-36.40888889 65.76355555-78.96177778 85.90222222-126.17955555 20.93511111-49.03822222 31.51644445-101.03466667 31.51644445-154.73777778 0-55.52355555-11.37777778-109.45422222-34.01955556-160.31288889z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M968.20337778 20.11591112H705.44042667c-22.17301333 0-41.92483556 15.16430222-47.14951111 37.33731555C642.36202666 124.73685332 582.08711111 173.03324444 512 173.03324444s-130.36202666-48.29639112-146.29091556-115.58001777c-5.22467555-22.17301333-24.84906667-37.33731556-47.14951111-37.33731555H55.79662222c-30.96576 0-56.06968889 25.10392889-56.06968888 56.06968888v321.12639999c0 30.96576 25.10392889 56.06968889 56.06968888 56.06968889h95.57333334v494.43271112c0 30.96576 25.10392889 56.06968889 56.06968889 56.06968888h609.1207111c30.96576 0 56.06968889-25.10392889 56.06968889-56.06968888V453.38168888h95.57333334c30.96576 0 56.06968889-25.10392889 56.06968888-56.06968889V76.1856c0-30.96576-25.10392889-56.06968889-56.06968888-56.06968888z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.94648889 239.80714666H523.46880001L373.99210666 96.82944c-1.91146667-1.78403556-4.46008889-2.80348444-7.00871111-2.80348445H43.05351111c-22.55530667 0-40.77795555 18.22264888-40.77795555 40.77795557v754.39217776c0 22.55530667 18.22264888 40.77795555 40.77795555 40.77795557h937.89297778c22.55530667 0 40.77795555-18.22264888 40.77795555-40.77795557V280.58510222c0-22.55530667-18.22264888-40.77795555-40.77795555-40.77795556z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M972.60904597 164.57058577L841.30587843 33.39070759c-18.86327195-18.86327195-44.1375906-29.34286748-70.64480282-29.3428675-26.75379095 0-51.90482023 10.47959553-70.76809219 29.3428675L558.60337778 174.68031322c-18.86327195 18.86327195-29.34286748 44.1375906-29.34286749 70.64480283 0 26.75379095 10.47959553 51.90482023 29.34286749 70.76809218l103.31648301 103.31648302c-24.28800376 53.50758189-57.69942011 101.59043198-99.24793416 143.13894603-41.42522469 41.67180341-89.63136414 75.08321976-143.13894603 99.61780223L316.21649759 558.84995649c-18.86327195-18.86327195-44.1375906-29.34286748-70.64480283-29.34286747-26.75379095 0-51.90482023 10.47959553-70.76809217 29.34286747L33.39070759 700.01627278c-18.86327195 18.86327195-29.34286748 44.1375906-29.3428675 70.76809217 0 26.75379095 10.47959553 51.90482023 29.3428675 70.76809219l131.05658883 131.05658883c30.08260365 30.205893 71.63111769 47.34311394 114.28923598 47.34311394 9.00012323 0 17.63037836-0.73973616 26.13734414-2.21920846 166.19405621-27.37023774 331.03192945-115.76870829 464.06114804-248.67463751C901.84095379 636.27567408 990.11613498 471.56109018 1017.85624079 304.87387654c8.38367642-50.91850535-8.50696579-103.31648302-45.24719482-140.30329077z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M910.60451556 640.96028445c-20.38897778-65.49959112-43.83630221-120.54983112-79.89930667-210.64362666C836.31217778 193.67708444 737.93535999 2.27555556 511.36284444 2.27555556 282.24170667 2.27555556 186.03121778 197.50001778 192.14791111 430.31665779c-36.19043555 90.22122667-59.51032888 144.88917333-79.89930667 210.64362666-43.32657778 139.53706668-29.30915556 197.26336001-18.60494222 198.53767111 22.9376 2.80348444 89.32920888-105.00323556 89.32920889-105.00323556 0 62.44124445 32.11264001 143.86972444 101.69002667 202.61546667-33.64181333 10.32192-109.20846222 38.10190221-91.24067556 68.55793777 14.52714667 24.59420444 250.01984 15.67402668 317.94062222 8.02816 67.92078222 7.64586667 303.41347556 16.56604444 317.94062223-8.02816 17.96778667-30.32860444-57.72629333-58.23601779-91.24067555-68.55793777 69.57738667-58.87317334 101.69002667-140.30165333 101.69002667-202.61546667 0 0 66.39160889 107.80672 89.32920888 105.00323556 10.83164445-1.40174222 24.84906667-59.12803556-18.47751111-198.53767111z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1016.86992592 199.24764445c-37.13706667 16.01991111-77.55093333 27.54939259-119.17842962 32.03982222 42.96248889-25.60758518 75.60912592-66.02145185 91.02222222-114.08118519-39.68568889 23.66577778-84.58998518 41.02068148-131.31472593 50.00154074C819.53374815 126.79395555 765.76995555 101.79318518 706.18074075 101.79318518c-114.688 0-206.92385185 92.96402963-206.92385186 207.04521482 0 16.01991111 1.94180741 32.03982222 5.09724444 47.45291852-171.72859259-8.98085925-324.88865185-91.02222222-426.71217778-216.63288889-17.96171852 30.82619259-28.15620741 66.02145185-28.1562074 104.49351112 0 71.84687408 36.53025185 135.19834075 92.23585185 172.45677036-33.98162963-1.33499259-66.02145185-10.92266667-93.57084445-26.33576296v2.54862222c0 100.6098963 71.1186963 183.98625185 165.90317037 203.1616-17.3549037 4.49042963-35.92343703 7.03905185-54.49197037 7.03905185-13.47128889 0-26.2144-1.33499259-39.07887407-3.15543704C146.69748148 681.90814815 223.03478518 741.49736297 313.93564445 743.43917037c-71.1186963 55.7056-160.19911111 88.4736-256.9253926 88.4736-17.3549037 0-33.37481482-0.60681482-50.00154074-2.54862222C98.75911111 888.22518518 207.62168889 922.20681482 324.85831111 922.20681482 705.45256297 922.20681482 913.71140741 606.90583703 913.71140741 333.23235555c0-8.98085925 0-17.96171852-0.60681482-26.94257777 40.2925037-29.4912 75.60912592-66.02145185 103.76533333-107.04213333z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M917.96720197 1.08889505H106.03279803C53.56084718 1.08889505 9.37393998 45.27580225 9.37393998 97.74775309v5.52336372c0 19.33177108 8.28504494 41.42522469 22.0934536 55.23363205l331.40179753 392.15879462v325.87843379c0 16.57008987 8.28504494 30.37849854 22.09345359 35.90186098l209.88780469 104.94390299 2.76168121 2.76168121c27.61681602 11.04672615 55.23363335-8.28504494 55.23363335-38.66354218V550.66354348l331.40179753-392.15879462c35.90186097-41.42522469 30.37849854-102.18222047-11.04672616-135.32240022-11.04672615-13.80840865-33.14017975-22.0934536-55.23363335-22.09345359z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M491.70164031 97.48884502a25.89076502 25.89076502 0 0 1 40.59671938 0L745.66415762 367.01171317a25.89076502 25.89076502 0 0 0 30.49932208 7.72839349l208.00640948-89.14190458a25.89076502 25.89076502 0 0 1 35.56096592 29.06238339l-115.18801541 554.96855704A103.56306132 103.56306132 0 0 1 803.14165689 952.14301275H220.85834311a103.56306132 103.56306132 0 0 1-101.4011828-82.51387024l-115.18801541-554.96855704a25.89076502 25.89076502 0 0 1 35.54802012-29.06238339l208.01935528 89.14190458a25.89076502 25.89076502 0 0 0 30.49932208-7.72839349l213.36579793-269.52286815z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M643.02466884 387.7801525c19.85376751-88.69205333 33.718272-152.84087467 41.61900049-192.57389433C704.52292267 95.17283515 652.90057916 2.27555515 550.58614084 2.27555515c-92.26012484 0-138.59407685 45.84971417-165.91530666 137.49816969l-0.70087152 2.67605334c-16.40038399 74.13942085-41.47882668 131.61085116-74.6746315 172.73287031a189.06953915 189.06953915 0 0 1-143.04142182 70.44391902l-26.17434983 0.5606965C77.66380049 387.52529067 27.76177817 438.90551468 27.76177817 501.84374084V881.55022182c0 77.4144 62.25009818 140.17422182 139.05282766 140.17422303h492.82707951c101.23127467 0 191.59267516-63.995904 225.93535999-159.98976l102.37815468-286.22301868c26.04691951-72.82688-11.39234134-153.15945284-83.63303784-179.42300483a138.04612267 138.04612267 0 0 0-47.17499733-8.30850884H643.02466884z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 512c140.82958222 0 254.86222222-114.03264 254.86222222-254.86222222S652.82958222 2.27555555 512 2.27555555a254.78940445 254.78940445 0 0 0-254.86222222 254.86222223C257.13777778 397.96736 371.17041778 512 512 512z m0 72.81777778c-170.10232889 0-509.72444445 97.57582222-509.72444445 291.27111111v145.63555556h1019.4488889v-145.63555556c0-193.69528889-339.62211555-291.27111111-509.72444445-291.27111111z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1019.81297778 564.50161779l-138.89991111-472.51456c-8.66531556-25.99594668-29.43658667-43.45400889-57.21656889-43.45400891s-50.33528889 15.67402668-59.00060446 41.66997334l-92.00526221 274.48661334H351.69166222L259.6864 90.33045333c-8.66531556-25.99594668-31.22062222-41.66997333-59.00060444-41.66997332s-50.33528889 17.33063112-57.2165689 43.45400887L4.69674667 564.50161779c-5.22467555 17.33063112 1.78403556 36.44529778 15.67402667 46.89464887l491.11950221 368.27591113 492.77610666-368.27591113c13.76256-10.32192 20.77127111-29.43658667 15.54659557-46.89464887z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M927.78951111 340.39277037c-12.01493333-47.81700741 12.01493333-124.03294815 89.08041481-150.97552592l-82.40545184-4.36906667s-31.19028148-109.22666667-174.27721483-118.9357037c-143.08693333-9.8304-236.65777778-3.64088889-236.65777777-3.6408889s106.07122963 67.47780741 63.5941926 187.74850371c-31.06891852 63.71555555-79.85682963 116.02299259-132.04290371 175.61220741-1.57771852 1.57771852-3.03407408 3.15543703-4.2477037 4.49042962C278.25493333 624.86755555 7.13007408 934.34311111 7.13007408 934.34311111c298.43152592 78.15774815 498.43768889-7.64586667 616.76657777-110.56165926 24.87940741-0.24272592 43.5693037-0.36408889 56.19105185-0.36408888 164.8109037 0 304.13558518-142.72284445 298.43152593-301.4656-3.88361482-109.1053037-38.71478518-133.74198518-50.72971852-181.5589926z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M997.8886764 504.17210418L537.2729208 43.89182982c-13.97838539-13.97838539-36.56745619-13.97838539-50.5458416 0L26.1113236 504.17210418c-13.41924998 13.41924998-21.02349164 31.64706454-21.02349163 50.65766867 0 39.47496036 32.09437288 71.56933323 71.56933324 71.56933323h48.53295408V954.83524937c0 19.79339373 15.99127289 35.78466661 35.78466663 35.78466662H440.43066677V740.12724968h125.24633315v250.49266631h297.34821416c19.79339373 0 35.78466661-15.99127289 35.78466663-35.78466662V626.39910608h48.53295408c19.01060414 0 37.23841869-7.49241457 50.65766869-21.02349163 27.84494371-27.95677079 27.84494371-73.24673948-0.11182708-101.20351027z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1009.13013121 349.27572283L674.72427717 14.86986879c-8.82158299-8.82158299-20.35749924-13.16451618-31.89341544-13.16451618s-23.07183245 4.34293316-31.89341547 13.16451618L392.29790453 233.6451272c-16.5574327-1.90003326-33.25058207-2.71433322-49.94373146-2.71433324-99.34459624 0-198.68919249 32.70771543-280.25490606 98.12314628-20.90036589 16.69314938-22.52896582 48.04369819-3.66434987 67.04403081l246.59717401 246.59717401-292.33368895 292.06225564c-3.52863319 3.52863319-5.83581644 8.27871636-6.24296642 13.30023282l-4.61436649 50.48659809c-1.22144996 12.75736619 8.95729967 23.6146991 21.57894918 23.6146991 0.6785833 0 1.35716662 0 2.03574992-0.13571666l50.48659809-4.61436649c5.02151649-0.40714999 9.77159962-2.71433322 13.30023282-6.24296643l292.33368896-292.33368896 246.59717402 246.59717401c8.82158299 8.82158299 20.35749924 13.16451618 31.89341544 13.16451618 13.16451618 0 26.19331567-5.70009979 35.15061536-16.82886604 76.40848044-95.40881307 108.16617924-214.83947521 95.27309638-330.33435417l218.63954175-218.63954173c17.50744934-17.37173267 17.50744934-45.8722316 0-63.51539759z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M976.62005979 160.47737905c-0.39452595-0.39452595-80.35178503 78.64217259-239.47725131 237.50462156l-111.6508437-111.65084369 237.89914752-237.89914752c-125.19623464-75.35445635-286.03131335-56.02268482-390.31767264 48.26367449-81.92988882 81.92988882-112.57140424 200.15616502-83.37648398 310.09739626l2.36715569 8.81107954-372.82702222 372.69551356c-8.15353628 8.15353628-8.15353628 21.56741857 0 29.72095487l185.95323084 185.95323084c8.15353628 8.15353628 21.56741857 8.15353628 29.72095485 0l372.56400493-372.56400493 8.81107953 2.3671557c110.07273989 29.32642892 228.29901608-1.18357785 310.36041356-83.24497533 104.41786795-104.2863593 123.74963948-265.12143802 49.97328693-390.05465535z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m220.16 343.26755556l-239.616 332.23111111c-14.44977778 20.13866667-44.37333333 20.13866667-58.82311111 0L291.84 481.16622222c-4.32355555-6.03022222 0-14.44977778 7.39555555-14.44977777h53.36177778c11.60533333 0 22.64177778 5.57511111 29.46844445 15.13244444l81.00977777 112.41244444 178.85866667-248.03555555c6.82666667-9.44355555 17.74933333-15.13244445 29.46844445-15.13244445H724.76444445c7.39555555 0 11.71911111 8.41955555 7.39555555 14.44977778z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m218.45333333 537.03111112c0 5.00622222-4.096 9.10222222-9.10222222 9.10222222H302.64888889c-5.00622222 0-9.10222222-4.096-9.10222222-9.10222222v-54.61333334c0-5.00622222 4.096-9.10222222 9.10222222-9.10222222h418.70222222c5.00622222 0 9.10222222 4.096 9.10222222 9.10222222v54.61333334z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m188.18844445 703.37422223l-75.09333334-0.34133333L512 570.48177778l-112.98133333 134.71288889-75.20711112 0.34133333c-5.00622222 0-9.10222222-3.98222222-9.10222222-9.10222222 0-2.16177778 0.79644445-4.20977778 2.16177778-5.91644445l148.02488889-176.35555555L316.87111111 337.92c-1.36533333-1.70666667-2.16177778-3.75466667-2.16177778-5.91644445 0-5.00622222 4.096-9.10222222 9.10222222-9.10222222l75.20711112 0.34133334L512 458.06933333l112.98133333-134.71288888 75.09333334-0.34133334c5.00622222 0 9.10222222 3.98222222 9.10222222 9.10222222 0 2.16177778-0.79644445 4.20977778-2.16177778 5.91644445L559.21777778 514.27555555l147.91111111 176.35555556c1.36533333 1.70666667 2.16177778 3.75466667 2.16177778 5.91644444 0 5.00622222-4.096 9.10222222-9.10222222 9.10222223z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m218.45333333 537.03111112c0 5.00622222-4.096 9.10222222-9.10222222 9.10222222H548.40888889v172.94222222c0 5.00622222-4.096 9.10222222-9.10222222 9.10222222h-54.61333334c-5.00622222 0-9.10222222-4.096-9.10222222-9.10222222V548.40888889H302.64888889c-5.00622222 0-9.10222222-4.096-9.10222222-9.10222222v-54.61333334c0-5.00622222 4.096-9.10222222 9.10222222-9.10222222h172.94222222V302.64888889c0-5.00622222 4.096-9.10222222 9.10222222-9.10222222h54.61333334c5.00622222 0 9.10222222 4.096 9.10222222 9.10222222v172.94222222h172.94222222c5.00622222 0 9.10222222 4.096 9.10222222 9.10222222v54.61333334z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m163.95377778 517.57511112L427.46311111 700.64355555c-1.59288889 1.13777778-3.41333333 1.70666667-5.34755556 1.70666667-5.00622222 0-9.10222222-4.096-9.10222222-9.10222222V331.88977778c0-1.93422222 0.56888889-3.75466667 1.70666667-5.34755556 2.95822222-4.096 8.64711111-5.00622222 12.74311111-2.048L675.95377778 505.17333333c0.79644445 0.56888889 1.47911111 1.25155555 2.048 2.048 2.95822222 3.98222222 2.048 9.67111111-2.048 12.62933334z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m200.81777778 666.39644445l-32.54044445 44.37333333c-2.95822222 4.096-8.64711111 4.89244445-12.74311111 1.93422222L479.34577778 577.76355555c-2.38933333-1.70666667-3.75466667-4.43733333-3.75466667-7.39555555V257.13777778c0-5.00622222 4.096-9.10222222 9.10222222-9.10222223h54.72711112c5.00622222 0 9.10222222 4.096 9.10222222 9.10222223v281.6l162.24711111 117.30488889c4.096 2.84444445 5.00622222 8.53333333 2.048 12.62933333z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M981.10577778 314.48177778c-25.6-61.09866667-62.464-115.93955555-109.34044445-163.04355556-46.87644445-46.99022222-101.60355555-83.968-162.70222222-109.568C646.59911111 15.58755555 580.38044445 2.27555555 512 2.27555555h-2.27555555c-68.83555555 0.34133333-135.39555555 13.99466667-198.08711112 40.84622223-60.52977778 25.94133333-114.80177778 62.80533333-161.22311111 109.79555555-46.42133333 46.99022222-82.83022222 101.60355555-108.08888889 162.47466667C16.27022222 378.42488889 3.072 445.44 3.41333333 514.38933333c0.34133333 78.96177778 19.22844445 157.35466667 54.49955556 227.44177778v172.94222222c0 28.89955555 23.43822222 52.33777778 52.224 52.33777778h172.71466666c69.97333333 35.38488889 148.13866667 54.272 226.98666667 54.61333334h2.38933333c68.03911111 0 133.91644445-13.19822222 196.03911112-39.02577778 60.75733333-25.37244445 115.37066667-61.78133333 162.13333333-108.31644445 46.87644445-46.53511111 83.74044445-100.92088889 109.568-161.56444444 26.73777778-62.80533333 40.39111111-129.59288889 40.73244445-198.54222223 0.22755555-69.29066667-13.19822222-136.53333333-39.59466667-199.79377777zM284.89955555 566.61333333c-30.03733333 0-54.49955555-24.46222222-54.49955555-54.61333333s24.46222222-54.61333333 54.49955555-54.61333333 54.49955555 24.46222222 54.49955556 54.61333333-24.34844445 54.61333333-54.49955556 54.61333333z m227.10044445 0c-30.03733333 0-54.49955555-24.46222222-54.49955555-54.61333333s24.46222222-54.61333333 54.49955555-54.61333333 54.49955555 24.46222222 54.49955555 54.61333333-24.46222222 54.61333333-54.49955555 54.61333333z m227.10044445 0c-30.03733333 0-54.49955555-24.46222222-54.49955556-54.61333333s24.46222222-54.61333333 54.49955556-54.61333333 54.49955555 24.46222222 54.49955555 54.61333333-24.46222222 54.61333333-54.49955555 54.61333333z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.2224823 3.06251924H43.7775177c-22.52048353 0-40.71499847 18.19451494-40.71499846 40.71499846v936.4449646c0 22.52048353 18.19451494 40.71499847 40.71499846 40.71499846h936.4449646c22.52048353 0 40.71499847-18.19451494 40.71499846-40.71499846V43.7775177c0-22.52048353-18.19451494-40.71499847-40.71499846-40.71499846zM745.4750693 325.8561164l-267.95558363 371.52436096c-16.15876501 22.52048353-49.62140436 22.52048353-65.78016939 0L253.07805667 477.51948567c-4.83490607-6.74342161 0-16.15876501 8.27023406-16.15876499h59.67291961c12.97790576 0 25.31963967 6.23448413 32.95370188 16.92217123l90.59087157 125.70755774 200.01242995-277.37092701c7.63406221-10.56045272 19.84856175-16.92217125 32.95370189-16.92217124H737.20483524c8.27023407 0 13.10514012 9.41534338 8.27023406 16.158765z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.2224823 3.06251924H43.7775177c-22.52048353 0-40.71499847 18.19451494-40.71499846 40.71499846v936.4449646c0 22.52048353 18.19451494 40.71499847 40.71499846 40.71499846h936.4449646c22.52048353 0 40.71499847-18.19451494 40.71499846-40.71499846V43.7775177c0-22.52048353-18.19451494-40.71499847-40.71499846-40.71499846zM756.28999077 542.53624885c0 5.59831228-4.58043732 10.17874961-10.17874962 10.17874962H277.88875885c-5.59831228 0-10.17874961-4.58043732-10.17874962-10.17874962v-61.0724977c0-5.59831228 4.58043732-10.17874961 10.17874962-10.17874962h468.2224823c5.59831228 0 10.17874961 4.58043732 10.17874962 10.17874962v61.0724977z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.2224823 3.06251924H43.7775177c-22.52048353 0-40.71499847 18.19451494-40.71499846 40.71499846v936.4449646c0 22.52048353 18.19451494 40.71499847 40.71499846 40.71499846h936.4449646c22.52048353 0 40.71499847-18.19451494 40.71499846-40.71499846V43.7775177c0-22.52048353-18.19451494-40.71499847-40.71499846-40.71499846zM720.79160148 697.63494611c5.59831228 6.61618726 0.8906406 16.6677025-7.76129658 16.66770249h-74.94104404c-5.98001539 0-11.70556205-2.67192177-15.64982754-7.25235911L512 575.36271635l-110.43943332 131.68757314c-3.81703111 4.58043732-9.54257777 7.25235911-15.64982754 7.25235911H310.9696951c-8.65193717 0-13.35960887-10.05151525-7.76129658-16.66770249L458.81603326 512 303.20839852 326.36505389c-5.59831228-6.61618726-0.8906406-16.6677025 7.76129658-16.66770249h74.94104404c5.98001539 0 11.70556205 2.67192177 15.64982754 7.25235911L512 448.63728365l110.43943332-131.68757314c3.81703111-4.58043732 9.54257777-7.25235911 15.64982754-7.25235911H713.0303049c8.65193717 0 13.35960887 10.05151525 7.76129658 16.66770249L565.18396674 512l155.60763474 185.63494611z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.2224823 3.06251924H43.7775177c-22.52048353 0-40.71499847 18.19451494-40.71499846 40.71499846v936.4449646c0 22.52048353 18.19451494 40.71499847 40.71499846 40.71499846h936.4449646c22.52048353 0 40.71499847-18.19451494 40.71499846-40.71499846V43.7775177c0-22.52048353-18.19451494-40.71499847-40.71499846-40.71499846zM677.02297814 523.19662459L423.31764398 722.70011704c-9.41534338 7.37959347-23.28388974 0.76340622-23.28388975-11.19662459V312.62374191c0-11.9600308 13.86854636-18.70345241 23.28388975-11.19662457l253.70533416 199.37625807c7.25235911 5.72554666 7.25235911 16.6677025 0 22.39324918z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.2224823 3.06251924H43.7775177c-22.52048353 0-40.71499847 18.19451494-40.71499846 40.71499846v936.4449646c0 22.52048353 18.19451494 40.71499847 40.71499846 40.71499846h936.4449646c22.52048353 0 40.71499847-18.19451494 40.71499846-40.71499846V43.7775177c0-22.52048353-18.19451494-40.71499847-40.71499846-40.71499846zM756.28999077 542.53624885c0 5.59831228-4.58043732 10.17874961-10.17874962 10.17874962H552.71499847v193.39624268c0 5.59831228-4.58043732 10.17874961-10.17874962 10.17874962h-61.0724977c-5.59831228 0-10.17874961-4.58043732-10.17874962-10.17874962V552.71499847H277.88875885c-5.59831228 0-10.17874961-4.58043732-10.17874962-10.17874962v-61.0724977c0-5.59831228 4.58043732-10.17874961 10.17874962-10.17874962h193.39624268V277.88875885c0-5.59831228 4.58043732-10.17874961 10.17874962-10.17874962h61.0724977c5.59831228 0 10.17874961 4.58043732 10.17874962 10.17874962v193.39624268h193.39624268c5.59831228 0 10.17874961 4.58043732 10.17874962 10.17874962v61.0724977z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M902.67315697 135.41705551L528.62204754 7.94466448C524.10877635 6.40354749 518.05438818 5.63298899 512 5.63298899s-12.10877635 0.7705585-16.62204754 2.31167549L121.32684303 135.41705551c-9.13662215 3.08223399-16.62204754 13.64989334-16.62204753 23.33691443v531.02488283c0 9.68702108 6.27454775 22.45627614 13.87005291 28.51066431L498.0198673 1013.9638196c3.85279247 2.9721542 8.8063828 4.51327118 13.87005291 4.51327118s10.12734022-1.54111698 13.87005291-4.51327118l379.4450189-295.67430252c7.59550517-5.94430839 13.87005291-18.71356345 13.87005291-28.51066431V158.75396994c0.22015956-9.68702108-7.26526581-20.14460066-16.40188796-23.33691443zM712.89560763 323.43332829L478.86598471 645.63685899c-7.04510625 9.68702108-21.57563786 9.68702108-28.6207441 0l-139.14084824-191.5388259c-4.18303182-5.8342286 0-13.9801327 7.15518603-13.9801327h60.76404132c5.61406904 0 11.0079785 2.75199463 14.31037204 7.26526582l71.22162091 97.97100864 166.11039557-228.74579323c3.30239355-4.51327118 8.58622323-7.26526581 14.31037204-7.26526581H705.7404216c7.15518602 0.11007979 11.33821785 8.25598388 7.15518603 14.09021248z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M959.86498307 186.28001231H797.00498922v-101.78749614c0-44.91373267-36.51626425-81.42999692-81.42999691-81.42999693H308.42500769c-44.91373267 0-81.42999692 36.51626425-81.42999691 81.42999693v101.78749614H64.13501693c-22.52048353 0-40.71499847 18.19451494-40.71499846 40.71499847v40.71499845c0 5.59831228 4.58043732 10.17874961 10.17874961 10.17874962h76.8495596l31.42688945 665.43575611c2.03574992 43.38692024 37.91584233 77.61296581 81.30276254 77.6129658h577.64404066c43.5141546 0 79.26701262-34.09881122 81.30276254-77.6129658l31.42688945-665.43575611H990.40123192c5.59831228 0 10.17874961-4.58043732 10.17874961-10.17874962v-40.71499845c0-22.52048353-18.19451494-40.71499847-40.71499846-40.71499847z m-254.46874039 0H318.60375732v-91.60874653h386.79248536v91.60874653z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.2224823 248.62485371H654.50249462V104.85001539c0-22.52048353-18.19451494-40.71499847-40.71499847-40.71499846H94.67126578v-50.89374808c0-5.59831228-4.58043732-10.17874961-10.17874961-10.17874961h-71.25124732c-5.59831228 0-10.17874961 4.58043732-10.17874961 10.17874961v997.5174623c0 5.59831228 4.58043732 10.17874961 10.17874961 10.17874961h71.25124732c5.59831228 0 10.17874961-4.58043732 10.17874961-10.17874961V674.85999383h315.54123807v143.77483833c0 22.52048353 18.19451494 40.71499847 40.71499846 40.71499846h529.29497999c22.52048353 0 40.71499847-18.19451494 40.71499846-40.71499846V289.33985217c0-22.52048353-18.19451494-40.71499847-40.71499846-40.71499846z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M804.63905145 265.16532183V94.67126578h109.42155836c5.59831228 0 10.17874961-4.58043732 10.17874961-10.17874961v-71.25124732c0-5.59831228-4.58043732-10.17874961-10.17874961-10.17874961H109.93939019c-5.59831228 0-10.17874961 4.58043732-10.17874961 10.17874961v71.25124732c0 5.59831228 4.58043732 10.17874961 10.17874961 10.17874961h109.42155836v170.49405605c0 103.6960117 53.94737296 194.92305513 135.3773699 246.83467817-81.42999692 51.91162303-135.37736988 143.13866646-135.3773699 246.83467817v170.49405605h-109.42155836c-5.59831228 0-10.17874961 4.58043732-10.17874961 10.17874961v71.25124732c0 5.59831228 4.58043732 10.17874961 10.17874961 10.17874961h804.12121962c5.59831228 0 10.17874961-4.58043732 10.17874961-10.17874961v-71.25124732c0-5.59831228-4.58043732-10.17874961-10.17874961-10.17874961h-109.42155836V758.83467817c0-103.6960117-53.94737296-194.92305513-135.3773699-246.83467817 81.42999692-51.91162303 135.37736988-143.13866646 135.3773699-246.83467817z',
+        special: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1020.928 448.44373333l-35.36213334-373.4528c-1.79200001-19.3536-17.2032-34.64533332-36.55679999-36.55679999L575.55626667 3.072h-0.47786666c-3.82293334 0-6.8096 1.19466667-9.07946669 3.46453333L6.53653333 565.99893332c-4.65919999 4.65919999-4.65919999 12.1856 0 16.84480001l434.61973334 434.61973334c2.26986667 2.26986667 5.25653333 3.46453333 8.48213333 3.46453333s6.21226667-1.19466667 8.48213333-3.46453333l559.46239999-559.46239999c2.38933332-2.5088 3.584-5.97333334 3.34506668-9.55733335zM735.40266668 362.66666667c-42.17173333 0-76.45866667-34.28693333-76.45866667-76.45866667s34.28693333-76.45866667 76.45866667-76.45866667 76.45866667 34.28693333 76.45866665 76.45866667-34.28693333 76.45866667-76.45866665 76.45866667z',
+        special: true,
+      },
+    ],
+  },
+
+  {
+    type: '线性',
+    children: [
+      {
+        viewBox: [1024, 1024],
+        path: 'M1009.55537674 75.96950982l-61.38012212-61.38012214c-4.48769762-4.48769762-11.870684-4.48769762-16.3583816 0L14.44462326 931.67210859c-4.48769762 4.48769762-4.48769762 11.870684 0 16.35838159l61.38012212 61.38012214c4.48769762 4.48769762 11.870684 4.48769762 16.3583816 0L1009.41061232 92.18312698c4.63246205-4.34293316 4.63246205-11.72591956 0.14476442-16.21361716zM210.88996692 419.35075905c114.94296453 0 208.46079213-93.51782759 208.46079213-208.46079213s-93.51782759-208.46079213-208.46079213-208.4607921-208.46079213 93.51782759-208.4607921 208.4607921 93.51782759 208.46079213 208.4607921 208.46079213z m0-312.69118816c57.47148228 0 104.23039605 46.75891379 104.23039607 104.23039603s-46.75891379 104.23039605-104.23039607 104.23039607-104.23039605-46.75891379-104.23039603-104.23039607 46.75891379-104.23039605 104.23039603-104.23039603zM813.11003308 604.64924095c-114.94296453 0-208.46079213 93.51782759-208.46079213 208.46079213s93.51782759 208.46079213 208.46079213 208.4607921 208.46079213-93.51782759 208.4607921-208.4607921-93.51782759-208.46079213-208.4607921-208.46079213z m0 312.69118816c-57.47148228 0-104.23039605-46.75891379-104.23039607-104.23039603s46.75891379-104.23039605 104.23039607-104.23039607 104.23039605 46.75891379 104.23039603 104.23039607-46.75891379 104.23039605-104.23039603 104.23039603z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1004.96017383 478.58365209L483.27851088 25.80594621c-4.00443838-3.45210207-9.11354943-5.3852792-14.49882864-5.38527921h-122.20441284c-10.21822208 0-14.91308089 12.70373557-7.18037228 19.33177152l483.57045622 419.77561022H14.8973037c-6.07569962 0-11.04672658 4.97102697-11.04672658 11.04672657v82.85044938c0 6.07569962 4.97102697 11.04672658 11.04672658 11.04672657h807.92996557L339.25681303 984.24756148c-7.7327086 6.76612003-3.0378498 19.33177153 7.18037229 19.33177152h126.34693531c2.62359757 0 5.24719513-0.96658859 7.18037228-2.76168164L1004.96017383 545.41634791c20.2983601-17.67476253 20.2983601-49.1579333 0-66.83269582z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1011.38217956 558.9924242L545.80649025 22.43713295c-17.81503843-20.62055629-49.79794206-20.62055629-67.75325638 0L12.61782044 558.9924242c-6.31241519 7.29434645-1.12220714 18.51641789 8.41655359 18.51641789h113.62347344c6.45269109 0 12.62483038-2.80551785 16.97338308-7.71517411L458.69516062 215.87758959V1005.77114384c0 6.1721393 5.04993216 11.22207145 11.22207144 11.22207145h84.16553588c6.1721393 0 11.22207145-5.04993216 11.22207144-11.22207145V215.87758959l307.06393007 353.91607839c4.20827679 4.90965626 10.38041608 7.71517413 16.97338308 7.71517411h113.62347344c9.53876074 0 14.72896878-11.22207145 8.41655359-18.51641789z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1009.1026963 459.52804874H201.17273073l483.57045624-419.77561022c7.7327086-6.76612003 3.0378498-19.33177153-7.18037229-19.33177152h-122.20441283c-5.3852792 0-10.49439025 1.93317715-14.49882866 5.38527921L19.03982617 478.58365209c-20.2983601 17.67476253-20.2983601 49.1579333 0 66.69461175L543.89742302 1000.81765136c2.07126124 1.79509307 4.55677472 2.76168163 7.18037228 2.76168164h126.3469353c10.21822208 0 14.91308089-12.70373557 7.18037228-19.33177152L201.17273073 564.47195126H1009.1026963c6.07569962 0 11.04672658-4.97102697 11.04672658-11.04672657v-82.85044938c0-6.07569962-4.97102697-11.04672658-11.04672658-11.04672657z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1002.96562597 446.49115791h-113.62347344c-6.45269109 0-12.62483038 2.80551785-16.97338308 7.71517411L565.30483938 808.12241041V18.22885616c0-6.1721393-5.04993216-11.22207145-11.22207144-11.22207145h-84.16553588c-6.1721393 0-11.22207145 5.04993216-11.22207144 11.22207145v789.89355425L151.63123055 454.20633202c-4.20827679-4.90965626-10.38041608-7.71517413-16.97338308-7.71517411h-113.62347344c-9.53876074 0-14.72896878 11.36234735-8.41655359 18.51641789L478.19350975 1001.56286705c17.81503843 20.62055629 49.79794206 20.62055629 67.75325638 0L1011.38217956 465.0075758c6.31241519-7.29434645 1.12220714-18.51641789-8.41655359-18.51641789z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M975.82443246 622.46726585H14.8973037c-6.07569962 0-11.04672658 4.97102697-11.04672658 11.04672658v82.85044937c0 6.07569962 4.97102697 11.04672658 11.04672658 11.04672659h835.6848661L651.32683905 980.10503902c-5.66144737 7.18037229-0.55233633 17.9509307 8.69929718 17.9509307h100.11095967c6.76612003 0 13.11798782-3.0378498 17.39859437-8.42312903l233.08593092-295.63802022c22.78387358-28.99765728 2.20934532-71.52755463-34.79718873-71.52755462zM1009.1026963 296.58883161H173.4178302l199.25533075-252.69387063c5.66144737-7.18037229 0.55233633-17.9509307-8.69929718-17.9509307h-100.11095967c-6.76612003 0-13.11798782 3.0378498-17.39859437 8.42312903L13.37837881 330.00517953c-22.78387358 28.99765728-2.20934532 71.52755463 34.65910466 71.52755462h961.06521283c6.07569962 0 11.04672658-4.97102697 11.04672658-11.04672658v-82.85044937c0-6.07569962-4.97102697-11.04672658-11.04672658-11.04672659z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M1010.75873115 64.13501693H13.24126885c-5.59831228 0-10.17874961 4.58043732-10.17874961 10.17874961v81.42999691c0 5.59831228 4.58043732 10.17874961 10.17874961 10.17874964h997.5174623c5.59831228 0 10.17874961-4.58043732 10.17874961-10.17874964v-81.42999691c0-5.59831228-4.58043732-10.17874961-10.17874961-10.17874961zM1010.75873115 858.07748691H13.24126885c-5.59831228 0-10.17874961 4.58043732-10.17874961 10.17874964v81.42999691c0 5.59831228 4.58043732 10.17874961 10.17874961 10.17874961h997.5174623c5.59831228 0 10.17874961-4.58043732 10.17874961-10.17874961v-81.42999691c0-5.59831228-4.58043732-10.17874961-10.17874961-10.17874964zM1010.75873115 461.10625194H13.24126885c-5.59831228 0-10.17874961 4.58043732-10.17874961 10.17874959v81.42999694c0 5.59831228 4.58043732 10.17874961 10.17874961 10.17874959h997.5174623c5.59831228 0 10.17874961-4.58043732 10.17874961-10.17874959v-81.42999694c0-5.59831228-4.58043732-10.17874961-10.17874961-10.17874959z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M591.98717801 512l405.34042913-483.16579151c6.79427767-8.02960089 1.08090782-20.22841761-9.41933951-20.2284176h-123.22349044c-7.25752386 0-14.20621693 3.24272343-18.99309439 8.80167789L511.38233839 415.95362022 177.07299399 17.40746878c-4.63246205-5.55895447-11.58115512-8.80167789-18.99309439-8.80167789H34.85640916c-10.50024731 0-16.21361717 12.19881672-9.41933952 20.2284176L430.77749876 512 25.43706964 995.16579151c-6.79427767 8.02960089-1.08090782 20.22841761 9.41933952 20.2284176h123.22349044c7.25752386 0 14.20621693-3.24272343 18.99309439-8.80167789l334.3093444-398.54615144 334.30934441 398.54615144c4.63246205 5.55895447 11.58115512 8.80167789 18.99309439 8.80167789h123.22349044c10.50024731 0 16.21361717-12.19881672 9.41933951-20.2284176L591.98717801 512z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M953.5488 832.61667556c-24.08448-57.08913778-58.74574221-108.31644445-102.70947556-152.28017777-43.96373333-43.96373333-95.19104-78.49756444-152.28017777-102.70947558-0.50972445-0.25486222-1.01944888-0.38229333-1.52917334-0.63715555C776.41955556 519.64586667 828.02915556 426.23886221 828.02915556 320.85333332c0-174.58062221-141.44853334-316.02915556-316.02915556-316.02915554S195.97084444 146.27271111 195.97084444 320.85333332c0 105.38552889 51.6096 198.79253333 130.99918223 256.26396447-0.50972445 0.25486222-1.01944888 0.38229333-1.52917334 0.63715555-57.08913778 24.08448-108.31644445 58.61831112-152.28017777 102.70947554-43.96373333 43.96373333-78.49756444 95.19104-102.70947556 152.28017779C46.74901333 888.55893332 34.13333334 947.8144 32.85902222 1008.72647111c-0.12743111 5.7344 4.46008889 10.44935111 10.19448889 10.44935111h76.45866667c5.60696888 0 10.06705778-4.46008889 10.19448889-9.93962666 2.54862221-98.37681778 42.05226667-190.50951112 111.88451555-260.34176001 72.25344-72.25344 168.20906666-112.01194667 270.40881778-112.01194667s198.15537778 39.75850667 270.40881778 112.01194667C852.24106667 818.72668444 891.74471111 910.85937779 894.29333333 1009.23619556c0.12743111 5.60696888 4.58752 9.93962667 10.19448889 9.93962666h76.45866667c5.7344 0 10.32192-4.71495112 10.19448889-10.44935111-1.27431111-60.91207112-13.88999112-120.16753779-37.59217778-176.10979555zM512 540.03484444c-58.49088 0-113.54112-22.81016889-154.95623111-64.22527999S292.81848888 379.34421333 292.81848888 320.85333332c0-58.49088 22.81016889-113.54112 64.22528001-154.9562311S453.50912 101.67182221 512 101.67182221s113.54112 22.81016889 154.95623111 64.22528001S731.18151112 262.36245333 731.18151112 320.85333332c0 58.49088-22.81016889 113.54112-64.22528001 154.95623113S570.49088 540.03484444 512 540.03484444z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M985.31555555 111.50222222H38.68444445c-20.13866667 0-36.40888889 16.27022222-36.4088889 36.40888889v728.17777778c0 20.13866667 16.27022222 36.40888889 36.4088889 36.40888889h946.6311111c20.13866667 0 36.40888889-16.27022222 36.4088889-36.40888889V147.91111111c0-20.13866667-16.27022222-36.40888889-36.4088889-36.40888889z m-45.5111111 126.06577778V830.57777778H84.19555555V237.568l-31.40266666-24.46222222 44.71466666-57.45777778 48.6968889 37.888h731.70488888l48.69688889-37.888 44.71466667 57.45777778-31.51644444 24.46222222z M877.90933333 193.42222222L512 477.86666667 146.09066667 193.42222222l-48.69688889-37.888-44.71466667 57.45777778 31.40266667 24.46222222 388.66488889 302.19377778c22.98311111 17.86311111 55.18222222 17.86311111 78.16533333 0L939.80444445 237.568l31.40266666-24.46222222-44.71466666-57.45777778-48.58311112 37.77422222z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M985.31555555 88.74666667H38.68444445c-20.13866667 0-36.40888889 16.27022222-36.4088889 36.40888888v564.33777778c0 20.13866667 16.27022222 36.40888889 36.4088889 36.40888889h432.35555555v127.43111111H275.34222222c-10.01244445 0-18.20444445 8.192-18.20444444 18.20444445v54.61333333c0 5.00622222 4.096 9.10222222 9.10222222 9.10222222h491.52c5.00622222 0 9.10222222-4.096 9.10222222-9.10222222v-54.61333333c0-10.01244445-8.192-18.20444445-18.20444444-18.20444445H552.96V725.90222222h432.35555555c20.13866667 0 36.40888889-16.27022222 36.4088889-36.40888889V125.15555555c0-20.13866667-16.27022222-36.40888889-36.4088889-36.40888888z m-45.5111111 555.23555555H84.19555555V170.66666667h855.6088889v473.31555555z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m0 932.97777778c-233.69955555 0-423.25333333-189.55377778-423.25333333-423.25333333 0-101.26222222 35.61244445-194.33244445 95.00444444-267.15022222l595.39911111 595.39911111C706.33244445 899.64088889 613.26222222 935.25333333 512 935.25333333z m328.24888889-156.10311111L244.84977778 183.75111111C317.66755555 124.35911111 410.73777778 88.74666667 512 88.74666667c233.69955555 0 423.25333333 189.55377778 423.25333333 423.25333333 0 101.26222222-35.61244445 194.33244445-95.00444444 267.15022222z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M901.80266667 257.82044445L656.95288889 12.97066667c-6.82666667-6.82666667-16.04266667-10.69511111-25.71377778-10.69511112H147.91111111c-20.13866667 0-36.40888889 16.27022222-36.40888889 36.4088889v946.6311111c0 20.13866667 16.27022222 36.40888889 36.40888889 36.4088889h728.17777778c20.13866667 0 36.40888889-16.27022222 36.40888889-36.4088889V283.648c0-9.67111111-3.86844445-19.00088889-10.69511111-25.82755555zM828.52977778 300.37333333H614.4V86.24355555L828.52977778 300.37333333z m2.048 639.43111112H193.42222222V84.19555555h343.60888889v245.76c0 26.39644445 21.39022222 47.78666667 47.78666667 47.78666667h245.76v562.06222223z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M981.07392 55.79662222H42.92608c-31.22062222 0-50.71758221 34.02410666-35.04355556 61.16693334L304.28728889 620.82616888V927.42542221c0 22.55530667 18.09521779 40.77795555 40.52309333 40.77795557h334.37923556c22.42787556 0 40.52309333-18.22264888 40.52309333-40.77795557V620.82616888L1016.24490667 116.96355556c15.54659555-27.14282666-3.95036444-61.16693333-35.17098667-61.16693334zM628.47203556 876.45297779H395.52796444V677.66044445h233.07150222v198.79253334z m12.23338666-301.50200891l-12.10595556 21.15356445h-233.19893332l-12.10595556-21.15356445L130.59868445 147.54702221h762.8026311L640.70542222 574.95096888z',
+        special: true,
+        outlined: true,
+      },
+      {
+        viewBox: [1024, 1024],
+        path: 'M980.62285431 4.54099753H654.39920987c-4.2719763 0-7.76722963 3.49525333-7.76722962 7.76722964v72.4941432c0 4.2719763 3.49525333 7.76722963 7.76722962 7.76722963h207.64393877L604.04167111 350.57107753c-64.72691358-49.83972347-143.69374815-76.7661195-226.67365136-76.7661195-99.54999309 0-193.27456395 38.83614815-263.5679921 109.25903012S4.54099753 547.08198717 4.54099753 646.63198025s38.83614815 193.27456395 109.25903012 263.5679921C184.09345581 980.62285431 277.81802667 1019.45900247 377.36801975 1019.45900247s193.27456395-38.83614815 263.5679921-109.25903012C711.35889383 839.90654419 750.19504197 746.18197333 750.19504197 646.63198025c0-82.9799032-26.92639605-161.68783013-76.63666567-226.41474372L931.4304 162.34521283V369.60079013c0 4.2719763 3.49525333 7.76722963 7.76722963 7.76722962h72.4941432c4.2719763 0 7.76722963-3.49525333 7.76722964-7.76722962V43.37714569c0-21.35988148-17.47626667-38.83614815-38.83614816-38.83614816zM377.36801975 921.07409383c-151.33152395 0-274.44211358-123.11058963-274.44211358-274.44211358s123.11058963-274.44211358 274.44211358-274.44211358 274.44211358 123.11058963 274.44211358 274.44211358-123.11058963 274.44211358-274.44211358 274.44211358z',
+        special: true,
+        outlined: true,
+      },
+    ],
+  }
+]

+ 1 - 0
src/configs/storage.ts

@@ -0,0 +1 @@
+export const LOCALSTORAGE_KEY_DISCARDED_DB = 'PPTIST_DISCARDED_DB'

+ 59 - 0
src/configs/symbol.ts

@@ -0,0 +1,59 @@
+export const SYMBOL_LIST = [
+  {
+    key: 'letter',
+    label: '字母',
+    children: [
+      'α', 'β', 'γ', 'δ', 'ϵ', 'ε', 'ζ', 'η', 'θ', 'ϑ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'π', 'ϖ', 'ρ', 'ϱ', 'σ', 'ς', 'τ', 'υ', 'ϕ', 'φ', 'χ', 'ψ', 'ω', 
+      'Γ', 'Δ', 'Θ', 'Λ', 'Ξ', 'Π', 'Σ', 'Υ', 'Φ', 'Ψ', 'Ω',
+      '𝐀', '𝐁', '𝐂', '𝐃', '𝐄', '𝐅', '𝐆', '𝐇', '𝐈', '𝐉', '𝐊', '𝐋', '𝐌', '𝐍', '𝐎', '𝐏', '𝐐', '𝐑', '𝐒', '𝐓', '𝐔', '𝐕', '𝐖', '𝐗', '𝐘', '𝐙',
+      '𝐚', '𝐛', '𝐜', '𝐝', '𝐞', '𝐟', '𝐠', '𝐡', '𝐢', '𝐣', '𝐤', '𝐥', '𝐦', '𝐧', '𝐨', '𝐩', '𝐪', '𝐫', '𝐬', '𝐭', '𝐮', '𝐯', '𝐰', '𝐱', '𝐲', '𝐳',
+      '𝓐', '𝓑', '𝓒', '𝓓', '𝓔', '𝓕', '𝓖', '𝓗', '𝓘', '𝓙', '𝓚', '𝓛', '𝓜', '𝓝', '𝓞', '𝓟', '𝓠', '𝓡', '𝓢', '𝓣', '𝓤', '𝓥', '𝓦', '𝓧', '𝓨', '𝓩',
+      '𝓪', '𝓫', '𝓬', '𝓭', '𝓮', '𝓯', '𝓰', '𝓱', '𝓲', '𝓳', '𝓴', '𝓵', '𝓶', '𝓷', '𝓸', '𝓹', '𝓺', '𝓻', '𝓼', '𝓽', '𝓾', '𝓿', '𝔀', '𝔁', '𝔂', '𝔃',
+    ],
+  },
+  {
+    key: 'number',
+    label: '序号',
+    children: [
+      '①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳',
+      '⑴', '⑵', '⑶', '⑷', '⑸', '⑹', '⑺', '⑻', '⑼', '⑽', '⑾', '⑿', '⒀', '⒁', '⒂', '⒃', '⒄', '⒅', '⒆', '⒇',
+      'º', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹', '₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉',
+      'Ⅰ', 'Ⅱ', 'Ⅲ', 'Ⅳ', 'Ⅴ', 'Ⅵ', 'Ⅶ', 'Ⅷ', 'Ⅸ', 'Ⅹ', 'Ⅺ', 'Ⅻ', 'Ⅼ', 'Ⅽ', 'Ⅾ', 'Ⅿ',
+      'ⅰ', 'ⅱ', 'ⅲ', 'ⅳ', 'ⅴ', 'ⅵ', 'ⅶ', 'ⅷ', 'ⅸ', 'ⅹ', 'ⅺ', 'ⅻ', 'ⅼ', 'ⅽ', 'ⅾ', 'ⅿ', 'ↀ', 'ↁ', 'ↂ',
+      '㊀', '㊁', '㊂', '㊃', '㊄', '㊅', '㊆', '㊇', '㊈', '㊉', '㈠', '㈡', '㈢', '㈣', '㈤', '㈥', '㈦', '㈧', '㈨', '㈩',
+      '𝟘', '𝟙', '𝟚', '𝟛', '𝟜', '𝟝', '𝟞', '𝟟', '𝟠', '𝟡',
+    ],
+  },
+  {
+    key: 'math',
+    label: '数学',
+    children: [
+      '+', '-', '×', '÷', '=', '~', '¬', '±', '%', '°', 'ǃ', '‰', '‱', '½', '⅓', '⅔', '¼', '¾',
+      '<', '>', 'l', 'o', 'g', 'l', 'g', 'l', 'n', '⨂', '⨁', '⨄', '⨃', '⨅', '⨆', '√', '∛', '∜', '∝', '∞',
+      '∟', '∠', '∡', '∢', '∧', '∨', '∩', '∪', '∫', '∬', '∭', '∮', '∯', '∰', '∱', '∲', '∳',
+      '∴', '∵', '∼', '∽', '∾', '∿', '≃', '≄', '≅', '≆', '≇', '≈', '≊', '≋', '≌', '≍', '≎', '≏', '≐', '≑', '≒', '≓', '≔', '≕',
+      '≤', '≥', '≦', '≧', '≨', '≩', '≪', '≫', '≺', '≻', '≼', '≽', '≾', '≿', '⊀', '⊁', '⊂', '⊃', '⊄', '⊅', '⊆', '⊇', '⊈', '⊉', '⊊', '⊋', '⊏', '⊐', '⊑', '⊒',
+      '⊓', '⊔', '⊢', '⊣', '⊤', '⊥', '⊦', '⊧', '⊨', '⊩', '⊪', '⊫', '⊬', '⊭', '⊮', '⊯', '⊲', '⊳', '⊴', '⊵', '⋀', '⋁', '⋂', '⋃', '⋉', '⋊',
+      '⋋', '⋌', '⟨', '⟩', '⟪', '⟫', '⟮', '⟯', '⧼', '⧽', '⦰',
+    ],
+  },
+  {
+    key: 'arrow',
+    label: '箭头',
+    children: [
+      '←', '↑', '→', '↓', '↔', '↕', '↖', '↗', '↘', '↙', '↚', '↛', '↜', '↝', '↞', '↟', '↠', '↡', '↢', '↣', '↤', '↥', '↦', '↧', '↨',
+      '↫', '↬', '↭', '↮', '↯', '↰', '↱', '↲', '↳', '↴', '↵', '↶', '↷', '↸', '↹', '↺', '↻', '↼', '↽', '↾', '↿', '⇀', '⇁', '⇂', '⇃',
+      '⇄', '⇅', '⇆', '⇇', '⇈', '⇉', '⇊', '⇋', '⇌', '⇍', '⇎', '⇏', '⇐', '⇑', '⇒', '⇓', '⇔', '⇕', '⇖', '⇗', '⇘', '⇙', '⇚', '⇛',
+      '⇜', '⇝', '⇞', '⇟', '⇠', '⇡', '⇢', '⇣', '⇤', '⇥', '⇦', '⇧', '⇨', '⇩', '⇪', '⇫', '⇬', '⇭', '⇮', '⇯', '⇰', '⇱', '⇲', '⇳', '⇴', '⇵',
+      '⇶', '⇷', '⇸', '⇹', '⇺', '⇻', '⇼', '⇽', '⇾', '⇿',
+    ],
+  },
+  {
+    key: 'graph',
+    label: '图形',
+    children: [
+      '▢', '▣', '▤', '▥', '▦', '▧', '▨', '▩', '▭', '▮', '▯', '▰', '▱', '▲', '▷', '▼', '◁',
+      '◈', '◉', '◍', '◐', '◑', '◒', '◓', '◔', '◕', '◧', '◨', '◩', '◪', '◫', '◬', '◭', '◮',
+    ],
+  },
+]

+ 93 - 0
src/configs/theme.ts

@@ -0,0 +1,93 @@
+export interface PresetTheme {
+  background: string
+  fontColor: string
+  fontname: string
+  colors: string[]
+}
+
+export const PRESET_THEMES: PresetTheme[] = [
+  {
+    background: '#ffffff',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#5b9bd5', '#ed7d31', '#a5a5a5', '#ffc000', '#4472c4', '#70ad47'],
+  },
+  {
+    background: '#ffffff',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#83992a', '#3c9670', '#44709d', '#a23b32', '#d87728', '#deb340'],
+  },
+  {
+    background: '#ffffff',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#e48312', '#bd582c', '#865640', '#9b8357', '#c2bc80', '#94a088'],
+  },
+  {
+    background: '#ffffff',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#bdc8df', '#003fa9', '#f5ba00', '#ff7567', '#7676d9', '#923ffc'],
+  },
+  {
+    background: '#ffffff',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#90c225', '#54a121', '#e6b91e', '#e86618', '#c42f19', '#918756'],
+  },
+  {
+    background: '#ffffff',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#1cade4', '#2683c6', '#27ced7', '#42ba97', '#3e8853', '#62a39f'],
+  },
+  {
+    background: '#e9efd6',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#a5300f', '#de7e18', '#9f8351', '#728653', '#92aa4c', '#6aac91'],
+  },
+  {
+    background: '#17444e',
+    fontColor: '#ffffff',
+    fontname: 'Microsoft Yahei',
+    colors: ['#b01513', '#ea6312', '#e6b729', '#6bab90', '#55839a', '#9e5d9d'],
+  },
+  {
+    background: '#36234d',
+    fontColor: '#ffffff',
+    fontname: 'Microsoft Yahei',
+    colors: ['#b31166', '#e33d6f', '#e45f3c', '#e9943a', '#9b6bf2', '#d63cd0'],
+  },
+  {
+    background: '#247fad',
+    fontColor: '#ffffff',
+    fontname: 'Microsoft Yahei',
+    colors: ['#052f61', '#a50e82', '#14967c', '#6a9e1f', '#e87d37', '#c62324'],
+  },
+  {
+    background: '#103f55',
+    fontColor: '#ffffff',
+    fontname: 'Microsoft Yahei',
+    colors: ['#40aebd', '#97e8d5', '#a1cf49', '#628f3e', '#f2df3a', '#fcb01c'],
+  },
+  {
+    background: '#242367',
+    fontColor: '#ffffff',
+    fontname: 'Microsoft Yahei',
+    colors: ['#ac3ec1', '#477bd1', '#46b298', '#90ba4c', '#dd9d31', '#e25345'],
+  },
+  {
+    background: '#e4b75e',
+    fontColor: '#333333',
+    fontname: 'Microsoft Yahei',
+    colors: ['#f0a22e', '#a5644e', '#b58b80', '#c3986d', '#a19574', '#c17529'],
+  },
+  {
+    background: '#333333',
+    fontColor: '#ffffff',
+    fontname: 'Microsoft Yahei',
+    colors: ['#bdc8df', '#003fa9', '#f5ba00', '#ff7567', '#7676d9', '#923ffc'],
+  },
+]

+ 16 - 0
src/global.d.ts

@@ -0,0 +1,16 @@
+interface HTMLElement {
+  webkitRequestFullScreen(options?: FullscreenOptions): Promise<void>
+  mozRequestFullScreen(options?: FullscreenOptions): Promise<void>
+  msRequestFullscreen(options?: FullscreenOptions): Promise<void>
+}
+
+interface Document {
+  webkitFullscreenElement: Element | null
+  mozFullScreenElement: Element | null
+  msFullscreenElement: Element | null
+  webkitCurrentFullScreenElement: Element | null
+
+  mozCancelFullScreen(): Promise<void>
+  webkitExitFullscreen(): Promise<void>
+  msExitFullscreen(): Promise<void>
+}

+ 106 - 0
src/hooks/useAddSlidesOrElements.ts

@@ -0,0 +1,106 @@
+import { storeToRefs } from 'pinia'
+import { nanoid } from 'nanoid'
+import { useSlidesStore, useMainStore } from '@/store'
+import type { PPTElement, Slide } from '@/types/slides'
+import { createSlideIdMap, createElementIdMap, getElementRange } from '@/utils/element'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
+
+export default () => {
+  const mainStore = useMainStore()
+  const slidesStore = useSlidesStore()
+  const { currentSlide } = storeToRefs(slidesStore)
+
+  const { addHistorySnapshot } = useHistorySnapshot()
+
+  /**
+   * 添加指定的元素数据(一组)
+   * @param elements 元素列表数据
+   */
+  const addElementsFromData = (elements: PPTElement[]) => {
+    const { groupIdMap, elIdMap } = createElementIdMap(elements)
+
+    const firstElement = elements[0]
+    let offset = 0
+    let lastSameElement: PPTElement | undefined
+    
+    do {
+      lastSameElement = currentSlide.value.elements.find(el => {
+        if (el.type !== firstElement.type) return false
+  
+        const { minX: oMinX, maxX: oMaxX, minY: oMinY, maxY: oMaxY } = getElementRange(el)
+        const { minX: nMinX, maxX: nMaxX, minY: nMinY, maxY: nMaxY } = getElementRange({
+          ...firstElement,
+          left: firstElement.left + offset,
+          top: firstElement.top + offset
+        })
+        if (
+          oMinX === nMinX &&
+          oMaxX === nMaxX &&
+          oMinY === nMinY &&
+          oMaxY === nMaxY
+        ) return true
+  
+        return false
+      })
+      if (lastSameElement) offset += 10
+
+    } while (lastSameElement)
+    
+    for (const element of elements) {
+      element.id = elIdMap[element.id]
+
+      element.left = element.left + offset
+      element.top = element.top + offset
+
+      if (element.groupId) element.groupId = groupIdMap[element.groupId]
+    }
+    slidesStore.addElement(elements)
+    mainStore.setActiveElementIdList(Object.values(elIdMap))
+    addHistorySnapshot()
+  }
+
+  /**
+   * 添加指定的页面数据
+   * @param slide 页面数据
+   */
+  const addSlidesFromData = (slides: Slide[]) => {
+    const slideIdMap = createSlideIdMap(slides)
+    const newSlides = slides.map(slide => {
+      const { groupIdMap, elIdMap } = createElementIdMap(slide.elements)
+
+      for (const element of slide.elements) {
+        element.id = elIdMap[element.id]
+        if (element.groupId) element.groupId = groupIdMap[element.groupId]
+		
+        // 若元素绑定了页面跳转链接
+        if (element.link && element.link.type === 'slide') {
+
+          // 待添加页面中包含该页面,则替换相关绑定关系
+          if (slideIdMap[element.link.target]) {
+            element.link.target = slideIdMap[element.link.target]
+          }
+          // 待添加页面中不包含该页面,则删除该元素绑定的页面跳转
+          else delete element.link
+        }
+      }
+      // 动画id替换
+      if (slide.animations) {
+        for (const animation of slide.animations) {
+          animation.id = nanoid(10)
+          animation.elId = elIdMap[animation.elId]
+        }
+      }
+      return {
+        ...slide,
+        id: slideIdMap[slide.id],
+      }
+    })
+    slidesStore.addSlide(newSlides)
+    addHistorySnapshot()
+  }
+
+  return {
+    addElementsFromData,
+    addSlidesFromData,
+  }
+}

+ 177 - 0
src/hooks/useAlignActiveElement.ts

@@ -0,0 +1,177 @@
+import { storeToRefs } from 'pinia'
+import { useMainStore, useSlidesStore } from '@/store'
+import type { PPTElement } from '@/types/slides'
+import { ElementAlignCommands } from '@/types/edit'
+import { getElementListRange, getRectRotatedOffset } from '@/utils/element'
+import useHistorySnapshot from './useHistorySnapshot'
+
+interface RangeMap {
+  [id: string]: ReturnType<typeof getElementListRange> 
+}
+
+export default () => {
+  const slidesStore = useSlidesStore()
+  const { activeElementIdList, activeElementList } = storeToRefs(useMainStore())
+  const { currentSlide } = storeToRefs(slidesStore)
+
+  const { addHistorySnapshot } = useHistorySnapshot()
+
+  /**
+   * 对齐选中的元素
+   * @param command 对齐方向
+   */
+  const alignActiveElement = (command: ElementAlignCommands) => {
+    const { minX, maxX, minY, maxY } = getElementListRange(activeElementList.value)
+    const elementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
+
+    // 如果所选择的元素为组合元素的成员,需要计算该组合的整体范围
+    const groupElementRangeMap: RangeMap = {}
+    for (const activeElement of activeElementList.value) {
+      if (activeElement.groupId && !groupElementRangeMap[activeElement.groupId]) {
+        const groupElements = activeElementList.value.filter(item => item.groupId === activeElement.groupId)
+        groupElementRangeMap[activeElement.groupId] = getElementListRange(groupElements)
+      }
+    }
+
+    // 根据不同的命令,计算对齐的位置
+    if (command === ElementAlignCommands.LEFT) {
+      elementList.forEach(element => {
+        if (activeElementIdList.value.includes(element.id)) {
+          if (!element.groupId) {
+            if ('rotate' in element && element.rotate) {
+              const { offsetX } = getRectRotatedOffset({
+                left: element.left,
+                top: element.top,
+                width: element.width,
+                height: element.height,
+                rotate: element.rotate,
+              })
+              element.left = minX - offsetX
+            }
+            else element.left = minX
+          }
+          else {
+            const range = groupElementRangeMap[element.groupId]
+            const offset = range.minX - minX
+            element.left = element.left - offset
+          }
+        }
+      })
+    }
+    else if (command === ElementAlignCommands.RIGHT) {
+      elementList.forEach(element => {
+        if (activeElementIdList.value.includes(element.id)) {
+          if (!element.groupId) {
+            const elWidth = element.type === 'line' ? Math.max(element.start[0], element.end[0]) : element.width
+            if ('rotate' in element && element.rotate) {
+              const { offsetX } = getRectRotatedOffset({
+                left: element.left,
+                top: element.top,
+                width: element.width,
+                height: element.height,
+                rotate: element.rotate,
+              })
+              element.left = maxX - elWidth + offsetX
+            }
+            else element.left = maxX - elWidth
+          }
+          else {
+            const range = groupElementRangeMap[element.groupId]
+            const offset = range.maxX - maxX
+            element.left = element.left - offset
+          }
+        }
+      })
+    }
+    else if (command === ElementAlignCommands.TOP) {
+      elementList.forEach(element => {
+        if (activeElementIdList.value.includes(element.id)) {
+          if (!element.groupId) {
+            if ('rotate' in element && element.rotate) {
+              const { offsetY } = getRectRotatedOffset({
+                left: element.left,
+                top: element.top,
+                width: element.width,
+                height: element.height,
+                rotate: element.rotate,
+              })
+              element.top = minY - offsetY
+            }
+            else element.top = minY
+          }
+          else {
+            const range = groupElementRangeMap[element.groupId]
+            const offset = range.minY - minY
+            element.top = element.top - offset
+          }
+        }
+      })
+    }
+    else if (command === ElementAlignCommands.BOTTOM) {
+      elementList.forEach(element => {
+        if (activeElementIdList.value.includes(element.id)) {
+          if (!element.groupId) {
+            const elHeight = element.type === 'line' ? Math.max(element.start[1], element.end[1]) : element.height
+            if ('rotate' in element && element.rotate) {
+              const { offsetY } = getRectRotatedOffset({
+                left: element.left,
+                top: element.top,
+                width: element.width,
+                height: element.height,
+                rotate: element.rotate,
+              })
+              element.top = maxY - elHeight + offsetY
+            }
+            else element.top = maxY - elHeight
+          }
+          else {
+            const range = groupElementRangeMap[element.groupId]
+            const offset = range.maxY - maxY
+            element.top = element.top - offset
+          }
+        }
+      })
+    }
+    else if (command === ElementAlignCommands.HORIZONTAL) {
+      const horizontalCenter = (minX + maxX) / 2
+      elementList.forEach(element => {
+        if (activeElementIdList.value.includes(element.id)) {
+          if (!element.groupId) {
+            const elWidth = element.type === 'line' ? Math.max(element.start[0], element.end[0]) : element.width
+            element.left = horizontalCenter - elWidth / 2
+          }
+          else {
+            const range = groupElementRangeMap[element.groupId]
+            const center = (range.maxX + range.minX) / 2
+            const offset = center - horizontalCenter
+            element.left = element.left - offset
+          }
+        }
+      })
+    }
+    else if (command === ElementAlignCommands.VERTICAL) {
+      const verticalCenter = (minY + maxY) / 2
+      elementList.forEach(element => {
+        if (activeElementIdList.value.includes(element.id)) {
+          if (!element.groupId) {
+            const elHeight = element.type === 'line' ? Math.max(element.start[1], element.end[1]) : element.height
+            element.top = verticalCenter - elHeight / 2
+          }
+          else {
+            const range = groupElementRangeMap[element.groupId]
+            const center = (range.maxY + range.minY) / 2
+            const offset = center - verticalCenter
+            element.top = element.top - offset
+          }
+        }
+      })
+    }
+
+    slidesStore.updateSlide({ elements: elementList })
+    addHistorySnapshot()
+  }
+
+  return {
+    alignActiveElement,
+  }
+}

+ 80 - 0
src/hooks/useAlignElementToCanvas.ts

@@ -0,0 +1,80 @@
+import { storeToRefs } from 'pinia'
+import { useMainStore, useSlidesStore } from '@/store'
+import type { PPTElement } from '@/types/slides'
+import { ElementAlignCommands } from '@/types/edit'
+import { getElementListRange } from '@/utils/element'
+import useHistorySnapshot from './useHistorySnapshot'
+
+export default () => {
+  const slidesStore = useSlidesStore()
+  const { activeElementIdList, activeElementList } = storeToRefs(useMainStore())
+  const { currentSlide, viewportRatio, viewportSize } = storeToRefs(slidesStore)
+
+  const { addHistorySnapshot } = useHistorySnapshot()
+
+  /**
+   * 将所有选中的元素对齐到画布
+   * @param command 对齐方向
+   */
+  const alignElementToCanvas = (command: ElementAlignCommands) => {
+    const viewportWidth = viewportSize.value
+    const viewportHeight = viewportSize.value * viewportRatio.value
+    const { minX, maxX, minY, maxY } = getElementListRange(activeElementList.value)
+  
+    const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
+    for (const element of newElementList) {
+      if (!activeElementIdList.value.includes(element.id)) continue
+      
+      // 水平垂直居中
+      if (command === ElementAlignCommands.CENTER) {
+        const offsetY = minY + (maxY - minY) / 2 - viewportHeight / 2
+        const offsetX = minX + (maxX - minX) / 2 - viewportWidth / 2
+        element.top = element.top - offsetY 
+        element.left = element.left - offsetX           
+      }
+
+      // 顶部对齐
+      if (command === ElementAlignCommands.TOP) {
+        const offsetY = minY - 0
+        element.top = element.top - offsetY            
+      }
+
+      // 垂直居中
+      else if (command === ElementAlignCommands.VERTICAL) {
+        const offsetY = minY + (maxY - minY) / 2 - viewportHeight / 2
+        element.top = element.top - offsetY            
+      }
+
+      // 底部对齐
+      else if (command === ElementAlignCommands.BOTTOM) {
+        const offsetY = maxY - viewportHeight
+        element.top = element.top - offsetY       
+      }
+      
+      // 左侧对齐
+      else if (command === ElementAlignCommands.LEFT) {
+        const offsetX = minX - 0
+        element.left = element.left - offsetX            
+      }
+
+      // 水平居中
+      else if (command === ElementAlignCommands.HORIZONTAL) {
+        const offsetX = minX + (maxX - minX) / 2 - viewportWidth / 2
+        element.left = element.left - offsetX            
+      }
+
+      // 右侧对齐
+      else if (command === ElementAlignCommands.RIGHT) {
+        const offsetX = maxX - viewportWidth
+        element.left = element.left - offsetX            
+      }
+    }
+
+    slidesStore.updateSlide({ elements: newElementList })
+    addHistorySnapshot()
+  }
+
+  return {
+    alignElementToCanvas,
+  }
+}

+ 91 - 0
src/hooks/useCombineElement.ts

@@ -0,0 +1,91 @@
+import { computed } from 'vue'
+import { storeToRefs } from 'pinia'
+import { nanoid } from 'nanoid'
+import { useMainStore, useSlidesStore } from '@/store'
+import type { PPTElement } from '@/types/slides'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
+
+export default () => {
+  const mainStore = useMainStore()
+  const slidesStore = useSlidesStore()
+  const { activeElementIdList, activeElementList, handleElementId } = storeToRefs(mainStore)
+  const { currentSlide } = storeToRefs(slidesStore)
+
+  const { addHistorySnapshot } = useHistorySnapshot()
+
+  /**
+   * 判断当前选中的元素是否可以组合
+   */
+  const canCombine = computed(() => {
+    if (activeElementList.value.length < 2) return false
+
+    const firstGroupId = activeElementList.value[0].groupId
+    if (!firstGroupId) return true
+
+    const inSameGroup = activeElementList.value.every(el => (el.groupId && el.groupId) === firstGroupId)
+    return !inSameGroup
+  })
+
+  /**
+   * 组合当前选中的元素:给当前选中的元素赋予一个相同的分组ID
+   */
+  const combineElements = () => {
+    if (!activeElementList.value.length) return
+
+    // 生成一个新元素列表进行后续操作
+    let newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
+
+    // 生成分组ID
+    const groupId = nanoid(10)
+
+    // 收集需要组合的元素列表,并赋上唯一分组ID
+    const combineElementList: PPTElement[] = []
+    for (const element of newElementList) {
+      if (activeElementIdList.value.includes(element.id)) {
+        element.groupId = groupId
+        combineElementList.push(element)
+      }
+    }
+
+    // 确保该组合内所有元素成员的层级是连续的,具体操作方法为:
+    // 先获取到该组合内最上层元素的层级,将本次需要组合的元素从新元素列表中移除,
+    // 再根据最上层元素的层级位置,将上面收集到的需要组合的元素列表一起插入到新元素列表中合适的位置
+    const combineElementMaxLevel = newElementList.findIndex(_element => _element.id === combineElementList[combineElementList.length - 1].id)
+    const combineElementIdList = combineElementList.map(_element => _element.id)
+    newElementList = newElementList.filter(_element => !combineElementIdList.includes(_element.id))
+
+    const insertLevel = combineElementMaxLevel - combineElementList.length + 1
+    newElementList.splice(insertLevel, 0, ...combineElementList)
+
+    slidesStore.updateSlide({ elements: newElementList })
+    addHistorySnapshot()
+  }
+
+  /**
+   * 取消组合元素:移除选中元素的分组ID
+   */
+  const uncombineElements = () => {
+    if (!activeElementList.value.length) return
+    const hasElementInGroup = activeElementList.value.some(item => item.groupId)
+    if (!hasElementInGroup) return
+    
+    const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
+    for (const element of newElementList) {
+      if (activeElementIdList.value.includes(element.id) && element.groupId) delete element.groupId
+    }
+    slidesStore.updateSlide({ elements: newElementList })
+
+    // 取消组合后,需要重置激活元素状态
+    // 默认重置为当前正在操作的元素,如果不存在则重置为空
+    const handleElementIdList = handleElementId.value ? [handleElementId.value] : []
+    mainStore.setActiveElementIdList(handleElementIdList)
+
+    addHistorySnapshot()
+  }
+
+  return {
+    canCombine,
+    combineElements,
+    uncombineElements,
+  }
+}

+ 55 - 0
src/hooks/useCopyAndPasteElement.ts

@@ -0,0 +1,55 @@
+import { storeToRefs } from 'pinia'
+import { useMainStore } from '@/store'
+import { copyText, readClipboard } from '@/utils/clipboard'
+import { encrypt } from '@/utils/crypto'
+import message from '@/utils/message'
+import usePasteTextClipboardData from '@/hooks/usePasteTextClipboardData'
+import useDeleteElement from './useDeleteElement'
+
+export default () => {
+  const mainStore = useMainStore()
+  const { activeElementIdList, activeElementList } = storeToRefs(mainStore)
+
+  const { pasteTextClipboardData } = usePasteTextClipboardData()
+  const { deleteElement } = useDeleteElement()
+
+  // 将选中元素数据加密后复制到剪贴板
+  const copyElement = () => {
+    if (!activeElementIdList.value.length) return
+
+    const text = encrypt(JSON.stringify({
+      type: 'elements',
+      data: activeElementList.value,
+    }))
+
+    copyText(text).then(() => {
+      mainStore.setEditorareaFocus(true)
+    })
+  }
+
+  // 将选中元素复制后删除(剪切)
+  const cutElement = () => {
+    copyElement()
+    deleteElement()
+  }
+
+  // 尝试将剪贴板元素数据解密后进行粘贴
+  const pasteElement = () => {
+    readClipboard().then(text => {
+      pasteTextClipboardData(text)
+    }).catch(err => message.warning(err))
+  }
+
+  // 将选中元素复制后立刻粘贴
+  const quickCopyElement = () => {
+    copyElement()
+    pasteElement()
+  }
+
+  return {
+    copyElement,
+    cutElement,
+    pasteElement,
+    quickCopyElement,
+  }
+}

+ 325 - 0
src/hooks/useCreateElement.ts

@@ -0,0 +1,325 @@
+import { storeToRefs } from 'pinia'
+import { nanoid } from 'nanoid'
+import { useMainStore, useSlidesStore } from '@/store'
+import { getImageSize } from '@/utils/image'
+import type { PPTLineElement, PPTElement, TableCell, TableCellStyle, PPTShapeElement, ChartType } from '@/types/slides'
+import { type ShapePoolItem, SHAPE_PATH_FORMULAS } from '@/configs/shapes'
+import type { LinePoolItem } from '@/configs/lines'
+import { CHART_DEFAULT_DATA } from '@/configs/chart'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
+
+interface CommonElementPosition {
+  top: number
+  left: number
+  width: number
+  height: number
+}
+
+interface LineElementPosition {
+  top: number
+  left: number
+  start: [number, number]
+  end: [number, number]
+}
+
+interface CreateTextData {
+  content?: string
+  vertical?: boolean
+}
+
+export default () => {
+  const mainStore = useMainStore()
+  const slidesStore = useSlidesStore()
+  const { creatingElement } = storeToRefs(mainStore)
+  const { theme, viewportRatio, viewportSize } = storeToRefs(slidesStore)
+
+  const { addHistorySnapshot } = useHistorySnapshot()
+
+  // 创建(插入)一个元素并将其设置为被选中元素
+  const createElement = (element: PPTElement, callback?: () => void) => {
+    slidesStore.addElement(element)
+    mainStore.setActiveElementIdList([element.id])
+
+    if (creatingElement.value) mainStore.setCreatingElement(null)
+
+    setTimeout(() => {
+      mainStore.setEditorareaFocus(true)
+    }, 0)
+
+    if (callback) callback()
+
+    addHistorySnapshot()
+  }
+
+  /**
+   * 创建图片元素
+   * @param src 图片地址
+   */
+  const createImageElement = (src: string) => {
+    getImageSize(src).then(({ width, height }) => {
+      const scale = height / width
+  
+      if (scale < viewportRatio.value && width > viewportSize.value) {
+        width = viewportSize.value
+        height = width * scale
+      }
+      else if (height > viewportSize.value * viewportRatio.value) {
+        height = viewportSize.value * viewportRatio.value
+        width = height / scale
+      }
+
+      createElement({
+        type: 'image',
+        id: nanoid(10),
+        src,
+        width,
+        height,
+        left: (viewportSize.value - width) / 2,
+        top: (viewportSize.value * viewportRatio.value - height) / 2,
+        fixedRatio: true,
+        rotate: 0,
+      })
+    })
+  }
+  
+  /**
+   * 创建图表元素
+   * @param chartType 图表类型
+   */
+  const createChartElement = (type: ChartType) => {
+    createElement({
+      type: 'chart',
+      id: nanoid(10),
+      chartType: type,
+      left: 300,
+      top: 81.25,
+      width: 400,
+      height: 400,
+      rotate: 0,
+      themeColors: [theme.value.themeColor],
+      textColor: theme.value.fontColor,
+      data: CHART_DEFAULT_DATA[type],
+    })
+  }
+  
+  /**
+   * 创建表格元素
+   * @param row 行数
+   * @param col 列数
+   */
+  const createTableElement = (row: number, col: number) => {
+    const style: TableCellStyle = {
+      fontname: theme.value.fontName,
+      color: theme.value.fontColor,
+    }
+    const data: TableCell[][] = []
+    for (let i = 0; i < row; i++) {
+      const rowCells: TableCell[] = []
+      for (let j = 0; j < col; j++) {
+        rowCells.push({ id: nanoid(10), colspan: 1, rowspan: 1, text: '', style })
+      }
+      data.push(rowCells)
+    }
+
+    const DEFAULT_CELL_WIDTH = 100
+    const DEFAULT_CELL_HEIGHT = 36
+
+    const colWidths: number[] = new Array(col).fill(1 / col)
+
+    const width = col * DEFAULT_CELL_WIDTH
+    const height = row * DEFAULT_CELL_HEIGHT
+
+    createElement({
+      type: 'table',
+      id: nanoid(10),
+      width,
+      height,
+      colWidths,
+      rotate: 0,
+      data,
+      left: (viewportSize.value - width) / 2,
+      top: (viewportSize.value * viewportRatio.value - height) / 2,
+      outline: {
+        width: 2,
+        style: 'solid',
+        color: '#eeece1',
+      },
+      theme: {
+        color: theme.value.themeColor,
+        rowHeader: true,
+        rowFooter: false,
+        colHeader: false,
+        colFooter: false,
+      },
+      cellMinHeight: 36,
+    })
+  }
+  
+  /**
+   * 创建文本元素
+   * @param position 位置大小信息
+   * @param content 文本内容
+   */
+  const createTextElement = (position: CommonElementPosition, data?: CreateTextData) => {
+    const { left, top, width, height } = position
+    const content = data?.content || ''
+    const vertical = data?.vertical || false
+
+    const id = nanoid(10)
+    createElement({
+      type: 'text',
+      id,
+      left, 
+      top, 
+      width, 
+      height,
+      content,
+      rotate: 0,
+      defaultFontName: theme.value.fontName,
+      defaultColor: theme.value.fontColor,
+      vertical,
+    }, () => {
+      setTimeout(() => {
+        const editorRef: HTMLElement | null = document.querySelector(`#editable-element-${id} .ProseMirror`)
+        if (editorRef) editorRef.focus()
+      }, 0)
+    })
+  }
+  
+  /**
+   * 创建形状元素
+   * @param position 位置大小信息
+   * @param data 形状路径信息
+   */
+  const createShapeElement = (position: CommonElementPosition, data: ShapePoolItem, supplement: Partial<PPTShapeElement> = {}) => {
+    const { left, top, width, height } = position
+    const newElement: PPTShapeElement = {
+      type: 'shape',
+      id: nanoid(10),
+      left, 
+      top, 
+      width, 
+      height,
+      viewBox: data.viewBox,
+      path: data.path,
+      fill: theme.value.themeColor,
+      fixedRatio: false,
+      rotate: 0,
+      ...supplement,
+    }
+    if (data.withborder) newElement.outline = theme.value.outline
+    if (data.special) newElement.special = true
+    if (data.pathFormula) {
+      newElement.pathFormula = data.pathFormula
+      newElement.viewBox = [width, height]
+
+      const pathFormula = SHAPE_PATH_FORMULAS[data.pathFormula]
+      if ('editable' in pathFormula && pathFormula.editable) {
+        newElement.path = pathFormula.formula(width, height, pathFormula.defaultValue!)
+        newElement.keypoints = pathFormula.defaultValue
+      }
+      else newElement.path = pathFormula.formula(width, height)
+    }
+    createElement(newElement)
+  }
+  
+  /**
+   * 创建线条元素
+   * @param position 位置大小信息
+   * @param data 线条的路径和样式
+   */
+  const createLineElement = (position: LineElementPosition, data: LinePoolItem) => {
+    const { left, top, start, end } = position
+
+    const newElement: PPTLineElement = {
+      type: 'line',
+      id: nanoid(10),
+      left, 
+      top, 
+      start,
+      end,
+      points: data.points,
+      color: theme.value.themeColor,
+      style: data.style,
+      width: 2,
+    }
+    if (data.isBroken) newElement.broken = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2]
+    if (data.isBroken2) newElement.broken2 = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2]
+    if (data.isCurve) newElement.curve = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2]
+    if (data.isCubic) newElement.cubic = [[(start[0] + end[0]) / 2, (start[1] + end[1]) / 2], [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2]]
+    createElement(newElement)
+  }
+  
+  /**
+   * 创建LaTeX元素
+   * @param svg SVG代码
+   */
+  const createLatexElement = (data: { path: string; latex: string; w: number; h: number; }) => {
+    createElement({
+      type: 'latex',
+      id: nanoid(10),
+      width: data.w,
+      height: data.h,
+      rotate: 0,
+      left: (viewportSize.value - data.w) / 2,
+      top: (viewportSize.value * viewportRatio.value - data.h) / 2,
+      path: data.path,
+      latex: data.latex,
+      color: theme.value.fontColor,
+      strokeWidth: 2,
+      viewBox: [data.w, data.h],
+      fixedRatio: true,
+    })
+  }
+  
+  /**
+   * 创建视频元素
+   * @param src 视频地址
+   */
+  const createVideoElement = (src: string) => {
+    createElement({
+      type: 'video',
+      id: nanoid(10),
+      width: 500,
+      height: 300,
+      rotate: 0,
+      left: (viewportSize.value - 500) / 2,
+      top: (viewportSize.value * viewportRatio.value - 300) / 2,
+      src,
+      autoplay: false,
+    })
+  }
+  
+  /**
+   * 创建音频元素
+   * @param src 音频地址
+   */
+  const createAudioElement = (src: string) => {
+    createElement({
+      type: 'audio',
+      id: nanoid(10),
+      width: 50,
+      height: 50,
+      rotate: 0,
+      left: (viewportSize.value - 50) / 2,
+      top: (viewportSize.value * viewportRatio.value - 50) / 2,
+      loop: false,
+      autoplay: false,
+      fixedRatio: true,
+      color: theme.value.themeColor,
+      src,
+    })
+  }
+
+  return {
+    createImageElement,
+    createChartElement,
+    createTableElement,
+    createTextElement,
+    createShapeElement,
+    createLineElement,
+    createLatexElement,
+    createVideoElement,
+    createAudioElement,
+  }
+}

+ 44 - 0
src/hooks/useDeleteElement.ts

@@ -0,0 +1,44 @@
+import { storeToRefs } from 'pinia'
+import { useMainStore, useSlidesStore } from '@/store'
+import type { PPTElement } from '@/types/slides'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
+
+export default () => {
+  const mainStore = useMainStore()
+  const slidesStore = useSlidesStore()
+  const { activeElementIdList, activeGroupElementId } = storeToRefs(mainStore)
+  const { currentSlide } = storeToRefs(slidesStore)
+
+  const { addHistorySnapshot } = useHistorySnapshot()
+
+  // 删除全部选中元素
+  // 组合元素成员中,存在被选中可独立操作的元素时,优先删除该元素。否则默认删除所有被选中的元素
+  const deleteElement = () => {
+    if (!activeElementIdList.value.length) return
+
+    let newElementList: PPTElement[] = []
+    if (activeGroupElementId.value) {
+      newElementList = currentSlide.value.elements.filter(el => el.id !== activeGroupElementId.value)
+    }
+    else {
+      newElementList = currentSlide.value.elements.filter(el => !activeElementIdList.value.includes(el.id))
+    }
+
+    mainStore.setActiveElementIdList([])
+    slidesStore.updateSlide({ elements: newElementList })
+    addHistorySnapshot()
+  }
+
+  // 删除内面内全部元素(无论是否选中)
+  const deleteAllElements = () => {
+    if (!currentSlide.value.elements.length) return
+    mainStore.setActiveElementIdList([])
+    slidesStore.updateSlide({ elements: [] })
+    addHistorySnapshot()
+  }
+
+  return {
+    deleteElement,
+    deleteAllElements,
+  }
+}

+ 855 - 0
src/hooks/useExport.ts

@@ -0,0 +1,855 @@
+import { computed, ref } from 'vue'
+import { storeToRefs } from 'pinia'
+import { trim } from 'lodash'
+import { saveAs } from 'file-saver'
+import pptxgen from 'pptxgenjs'
+import tinycolor from 'tinycolor2'
+import { toPng, toJpeg } from 'html-to-image'
+import { useSlidesStore } from '@/store'
+import type { PPTElementOutline, PPTElementShadow, PPTElementLink, Slide } from '@/types/slides'
+import { getElementRange, getLineElementPath, getTableSubThemeColor } from '@/utils/element'
+import { type AST, toAST } from '@/utils/htmlParser'
+import { type SvgPoints, toPoints } from '@/utils/svgPathParser'
+import { encrypt } from '@/utils/crypto'
+import { svg2Base64 } from '@/utils/svg2Base64'
+import message from '@/utils/message'
+
+interface ExportImageConfig {
+  quality: number
+  width: number
+  fontEmbedCSS?: string
+}
+
+export default () => {
+  const slidesStore = useSlidesStore()
+  const { slides, theme, viewportRatio, title, viewportSize } = storeToRefs(slidesStore)
+
+  const ratioPx2Inch = computed(() => {
+    return 96 * (viewportSize.value / 960)
+  })
+  const ratioPx2Pt = computed(() => {
+    return 96 / 72 * (viewportSize.value / 960)
+  })
+
+  const exporting = ref(false)
+
+  // 导出图片
+  const exportImage = (domRef: HTMLElement, format: string, quality: number, ignoreWebfont = true) => {
+    exporting.value = true
+    const toImage = format === 'png' ? toPng : toJpeg
+
+    const foreignObjectSpans = domRef.querySelectorAll('foreignObject [xmlns]')
+    foreignObjectSpans.forEach(spanRef => spanRef.removeAttribute('xmlns'))
+
+    setTimeout(() => {
+      const config: ExportImageConfig = {
+        quality,
+        width: 1600,
+      }
+
+      if (ignoreWebfont) config.fontEmbedCSS = ''
+
+      toImage(domRef, config).then(dataUrl => {
+        exporting.value = false
+        saveAs(dataUrl, `${title.value}.${format}`)
+      }).catch(() => {
+        exporting.value = false
+        message.error('导出图片失败')
+      })
+    }, 200)
+  }
+  
+  // 导出pptist文件(特有 .pptist 后缀文件)
+  const exportSpecificFile = (_slides: Slide[]) => {
+    const blob = new Blob([encrypt(JSON.stringify(_slides))], { type: '' })
+    saveAs(blob, `${title.value}.pptist`)
+  }
+  
+  // 导出JSON文件
+  const exportJSON = () => {
+    const json = {
+      title: title.value,
+      width: viewportSize.value,
+      height: viewportSize.value * viewportRatio.value,
+      slides: slides.value,
+    }
+    const blob = new Blob([JSON.stringify(json)], { type: '' })
+    saveAs(blob, `${title.value}.json`)
+  }
+
+  // 格式化颜色值为 透明度 + HexString,供pptxgenjs使用
+  const formatColor = (_color: string) => {
+    const c = tinycolor(_color)
+    const alpha = c.getAlpha()
+    const color = alpha === 0 ? '#ffffff' : c.setAlpha(1).toHexString()
+    return {
+      alpha,
+      color,
+    }
+  }
+
+  type FormatColor = ReturnType<typeof formatColor>
+
+  // 将HTML字符串格式化为pptxgenjs所需的格式
+  // 核心思路:将HTML字符串按样式分片平铺,每个片段需要继承祖先元素的样式信息,遇到块级元素需要换行
+  const formatHTML = (html: string) => {
+    const ast = toAST(html)
+    let bulletFlag = false
+    let indent = 0
+
+    const slices: pptxgen.TextProps[] = []
+    const parse = (obj: AST[], baseStyleObj: { [key: string]: string } = {}) => {
+
+      for (const item of obj) {
+        const isBlockTag = 'tagName' in item && ['div', 'li', 'p'].includes(item.tagName)
+
+        if (isBlockTag && slices.length) {
+          const lastSlice = slices[slices.length - 1]
+          if (!lastSlice.options) lastSlice.options = {}
+          lastSlice.options.breakLine = true
+        }
+
+        const styleObj = { ...baseStyleObj }
+        const styleAttr = 'attributes' in item ? item.attributes.find(attr => attr.key === 'style') : null
+        if (styleAttr && styleAttr.value) {
+          const styleArr = styleAttr.value.split(';')
+          for (const styleItem of styleArr) {
+            const [_key, _value] = styleItem.split(': ')
+            const [key, value] = [trim(_key), trim(_value)]
+            if (key && value) styleObj[key] = value
+          }
+        }
+
+        if ('tagName' in item) {
+          if (item.tagName === 'em') {
+            styleObj['font-style'] = 'italic'
+          }
+          if (item.tagName === 'strong') {
+            styleObj['font-weight'] = 'bold'
+          }
+          if (item.tagName === 'sup') {
+            styleObj['vertical-align'] = 'super'
+          }
+          if (item.tagName === 'sub') {
+            styleObj['vertical-align'] = 'sub'
+          }
+          if (item.tagName === 'a') {
+            const attr = item.attributes.find(attr => attr.key === 'href')
+            styleObj['href'] = attr?.value || ''
+          }
+          if (item.tagName === 'ul') {
+            styleObj['list-type'] = 'ul'
+          }
+          if (item.tagName === 'ol') {
+            styleObj['list-type'] = 'ol'
+          }
+          if (item.tagName === 'li') {
+            bulletFlag = true
+          }
+          if (item.tagName === 'p') {
+            if ('attributes' in item) {
+              const dataIndentAttr = item.attributes.find(attr => attr.key === 'data-indent')
+              if (dataIndentAttr && dataIndentAttr.value) indent = +dataIndentAttr.value
+            }
+          }
+        }
+
+        if ('tagName' in item && item.tagName === 'br') {
+          slices.push({ text: '', options: { breakLine: true } })
+        }
+        else if ('content' in item) {
+          const text = item.content.replace(/&nbsp;/g, ' ').replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&').replace(/\n/g, '')
+          const options: pptxgen.TextPropsOptions = {}
+
+          if (styleObj['font-size']) {
+            options.fontSize = parseInt(styleObj['font-size']) / ratioPx2Pt.value
+          }
+          if (styleObj['color']) {
+            options.color = formatColor(styleObj['color']).color
+          }
+          if (styleObj['background-color']) {
+            options.highlight = formatColor(styleObj['background-color']).color
+          }
+          if (styleObj['text-decoration-line']) {
+            if (styleObj['text-decoration-line'].indexOf('underline') !== -1) {
+              options.underline = {
+                color: options.color || '#000000',
+                style: 'sng',
+              }
+            }
+            if (styleObj['text-decoration-line'].indexOf('line-through') !== -1) {
+              options.strike = 'sngStrike'
+            }
+          }
+          if (styleObj['text-decoration']) {
+            if (styleObj['text-decoration'].indexOf('underline') !== -1) {
+              options.underline = {
+                color: options.color || '#000000',
+                style: 'sng',
+              }
+            }
+            if (styleObj['text-decoration'].indexOf('line-through') !== -1) {
+              options.strike = 'sngStrike'
+            }
+          }
+          if (styleObj['vertical-align']) {
+            if (styleObj['vertical-align'] === 'super') options.superscript = true
+            if (styleObj['vertical-align'] === 'sub') options.subscript = true
+          }
+          if (styleObj['text-align']) options.align = styleObj['text-align'] as pptxgen.HAlign
+          if (styleObj['font-weight']) options.bold = styleObj['font-weight'] === 'bold'
+          if (styleObj['font-style']) options.italic = styleObj['font-style'] === 'italic'
+          if (styleObj['font-family']) options.fontFace = styleObj['font-family']
+          if (styleObj['href']) options.hyperlink = { url: styleObj['href'] }
+
+          if (bulletFlag && styleObj['list-type'] === 'ol') {
+            options.bullet = { type: 'number', indent: (options.fontSize || 20) * 1.25 }
+            options.paraSpaceBefore = 0.1
+            bulletFlag = false
+          }
+          if (bulletFlag && styleObj['list-type'] === 'ul') {
+            options.bullet = { indent: (options.fontSize || 20) * 1.25 }
+            options.paraSpaceBefore = 0.1
+            bulletFlag = false
+          }
+          if (indent) {
+            options.indentLevel = indent
+            indent = 0
+          }
+
+          slices.push({ text, options })
+        }
+        else if ('children' in item) parse(item.children, styleObj)
+      }
+    }
+    parse(ast)
+    return slices
+  }
+
+  type Points = Array<
+    | { x: number; y: number; moveTo?: boolean }
+    | { x: number; y: number; curve: { type: 'arc'; hR: number; wR: number; stAng: number; swAng: number } }
+    | { x: number; y: number; curve: { type: 'quadratic'; x1: number; y1: number } }
+    | { x: number; y: number; curve: { type: 'cubic'; x1: number; y1: number; x2: number; y2: number } }
+    | { close: true }
+  >
+
+  // 将SVG路径信息格式化为pptxgenjs所需要的格式
+  const formatPoints = (points: SvgPoints, scale = { x: 1, y: 1 }): Points => {
+    return points.map(point => {
+      if (point.close !== undefined) {
+        return { close: true }
+      }
+      else if (point.type === 'M') {
+        return {
+          x: point.x / ratioPx2Inch.value * scale.x,
+          y: point.y / ratioPx2Inch.value * scale.y,
+          moveTo: true,
+        }
+      }
+      else if (point.curve) {
+        if (point.curve.type === 'cubic') {
+          return {
+            x: point.x / ratioPx2Inch.value * scale.x,
+            y: point.y / ratioPx2Inch.value * scale.y,
+            curve: {
+              type: 'cubic',
+              x1: (point.curve.x1 as number) / ratioPx2Inch.value * scale.x,
+              y1: (point.curve.y1 as number) / ratioPx2Inch.value * scale.y,
+              x2: (point.curve.x2 as number) / ratioPx2Inch.value * scale.x,
+              y2: (point.curve.y2 as number) / ratioPx2Inch.value * scale.y,
+            },
+          }
+        }
+        else if (point.curve.type === 'quadratic') {
+          return {
+            x: point.x / ratioPx2Inch.value * scale.x,
+            y: point.y / ratioPx2Inch.value * scale.y,
+            curve: {
+              type: 'quadratic',
+              x1: (point.curve.x1 as number) / ratioPx2Inch.value * scale.x,
+              y1: (point.curve.y1 as number) / ratioPx2Inch.value * scale.y,
+            },
+          }
+        }
+      }
+      return {
+        x: point.x / ratioPx2Inch.value * scale.x,
+        y: point.y / ratioPx2Inch.value * scale.y,
+      }
+    })
+  }
+
+  // 获取阴影配置
+  const getShadowOption = (shadow: PPTElementShadow): pptxgen.ShadowProps => {
+    const c = formatColor(shadow.color)
+    const { h, v } = shadow
+
+    let offset = 4
+    let angle = 45
+
+    if (h === 0 && v === 0) {
+      offset = 4
+      angle = 45
+    }
+    else if (h === 0) {
+      if (v > 0) {
+        offset = v
+        angle = 90
+      }
+      else {
+        offset = -v
+        angle = 270
+      }
+    }
+    else if (v === 0) {
+      if (h > 0) {
+        offset = h
+        angle = 1
+      }
+      else {
+        offset = -h
+        angle = 180
+      }
+    }
+    else if (h > 0 && v > 0) {
+      offset = Math.max(h, v)
+      angle = 45
+    }
+    else if (h > 0 && v < 0) {
+      offset = Math.max(h, -v)
+      angle = 315
+    }
+    else if (h < 0 && v > 0) {
+      offset = Math.max(-h, v)
+      angle = 135
+    }
+    else if (h < 0 && v < 0) {
+      offset = Math.max(-h, -v)
+      angle = 225
+    }
+
+    return {
+      type: 'outer',
+      color: c.color.replace('#', ''),
+      opacity: c.alpha,
+      blur: shadow.blur / ratioPx2Pt.value,
+      offset,
+      angle,
+    }
+  }
+
+  const dashTypeMap = {
+    'solid': 'solid',
+    'dashed': 'dash',
+    'dotted': 'sysDot',
+  }
+
+  // 获取边框配置
+  const getOutlineOption = (outline: PPTElementOutline): pptxgen.ShapeLineProps => {
+    const c = formatColor(outline?.color || '#000000')
+    
+    return {
+      color: c.color, 
+      transparency: (1 - c.alpha) * 100,
+      width: (outline.width || 1) / ratioPx2Pt.value, 
+      dashType: outline.style ? dashTypeMap[outline.style] as 'solid' | 'dash' | 'sysDot' : 'solid',
+    }
+  }
+
+  // 获取超链接配置
+  const getLinkOption = (link: PPTElementLink): pptxgen.HyperlinkProps | null => {
+    const { type, target } = link
+    if (type === 'web') return { url: target }
+    if (type === 'slide') {
+      const index = slides.value.findIndex(slide => slide.id === target)
+      if (index !== -1) return { slide: index + 1 }
+    }
+
+    return null
+  }
+
+  // 判断是否为Base64图片地址
+  const isBase64Image = (url: string) => {
+    const regex = /^data:image\/[^;]+;base64,/
+    return url.match(regex) !== null
+  }
+
+  // 导出PPTX文件
+  const exportPPTX = (_slides: Slide[], masterOverwrite: boolean, ignoreMedia: boolean) => {
+    exporting.value = true
+    const pptx = new pptxgen()
+
+    if (viewportRatio.value === 0.625) pptx.layout = 'LAYOUT_16x10'
+    else if (viewportRatio.value === 0.75) pptx.layout = 'LAYOUT_4x3'
+    else if (viewportRatio.value === 0.70710678) {
+      pptx.defineLayout({ name: 'A3', width: 10, height: 7.0710678 })
+      pptx.layout = 'A3'
+    }
+    else if (viewportRatio.value === 1.41421356) {
+      pptx.defineLayout({ name: 'A3_V', width: 10, height: 14.1421356 })
+      pptx.layout = 'A3_V'
+    }
+    else pptx.layout = 'LAYOUT_16x9'
+
+    if (masterOverwrite) {
+      const { color: bgColor, alpha: bgAlpha } = formatColor(theme.value.backgroundColor)
+      pptx.defineSlideMaster({
+        title: 'PPTIST_MASTER',
+        background: { color: bgColor, transparency: (1 - bgAlpha) * 100 },
+      })
+    }
+
+    for (const slide of _slides) {
+      const pptxSlide = pptx.addSlide()
+
+      if (slide.background) {
+        const background = slide.background
+        if (background.type === 'image' && background.image) {
+          if (isBase64Image(background.image.src)) pptxSlide.background = { data: background.image.src }
+          else pptxSlide.background = { path: background.image.src }
+        }
+        else if (background.type === 'solid' && background.color) {
+          const c = formatColor(background.color)
+          pptxSlide.background = { color: c.color, transparency: (1 - c.alpha) * 100 }
+        }
+        else if (background.type === 'gradient' && background.gradient) {
+          const colors = background.gradient.colors
+          const color1 = colors[0].color
+          const color2 = colors[colors.length - 1].color
+          const color = tinycolor.mix(color1, color2).toHexString()
+          const c = formatColor(color)
+          pptxSlide.background = { color: c.color, transparency: (1 - c.alpha) * 100 }
+        }
+      }
+      if (slide.remark) pptxSlide.addNotes(slide.remark)
+
+      if (!slide.elements) continue
+
+      for (const el of slide.elements) {
+        if (el.type === 'text') {
+          const textProps = formatHTML(el.content)
+
+          const options: pptxgen.TextPropsOptions = {
+            x: el.left / ratioPx2Inch.value,
+            y: el.top / ratioPx2Inch.value,
+            w: el.width / ratioPx2Inch.value,
+            h: el.height / ratioPx2Inch.value,
+            fontSize: 20 / ratioPx2Pt.value,
+            fontFace: '微软雅黑',
+            color: '#000000',
+            valign: 'top',
+            margin: 10 / ratioPx2Pt.value,
+            paraSpaceBefore: 5 / ratioPx2Pt.value,
+            lineSpacingMultiple: 1.5 / 1.25,
+            autoFit: true,
+          }
+          if (el.rotate) options.rotate = el.rotate
+          if (el.wordSpace) options.charSpacing = el.wordSpace / ratioPx2Pt.value
+          if (el.lineHeight) options.lineSpacingMultiple = el.lineHeight / 1.25
+          if (el.fill) {
+            const c = formatColor(el.fill)
+            const opacity = el.opacity === undefined ? 1 : el.opacity
+            options.fill = { color: c.color, transparency: (1 - c.alpha * opacity) * 100 }
+          }
+          if (el.defaultColor) options.color = formatColor(el.defaultColor).color
+          if (el.defaultFontName) options.fontFace = el.defaultFontName
+          if (el.shadow) options.shadow = getShadowOption(el.shadow)
+          if (el.outline?.width) options.line = getOutlineOption(el.outline)
+          if (el.opacity !== undefined) options.transparency = (1 - el.opacity) * 100
+          if (el.paragraphSpace !== undefined) options.paraSpaceBefore = el.paragraphSpace / ratioPx2Pt.value
+          if (el.vertical) options.vert = 'eaVert'
+
+          pptxSlide.addText(textProps, options)
+        }
+
+        else if (el.type === 'image') {
+          const options: pptxgen.ImageProps = {
+            x: el.left / ratioPx2Inch.value,
+            y: el.top / ratioPx2Inch.value,
+            w: el.width / ratioPx2Inch.value,
+            h: el.height / ratioPx2Inch.value,
+          }
+          if (isBase64Image(el.src)) options.data = el.src
+          else options.path = el.src
+
+          if (el.flipH) options.flipH = el.flipH
+          if (el.flipV) options.flipV = el.flipV
+          if (el.rotate) options.rotate = el.rotate
+          if (el.link) {
+            const linkOption = getLinkOption(el.link)
+            if (linkOption) options.hyperlink = linkOption
+          }
+          if (el.filters?.opacity) options.transparency = 100 - parseInt(el.filters?.opacity)
+          if (el.clip) {
+            if (el.clip.shape === 'ellipse') options.rounding = true
+
+            const [start, end] = el.clip.range
+            const [startX, startY] = start
+            const [endX, endY] = end
+
+            const originW = el.width / ((endX - startX) / ratioPx2Inch.value)
+            const originH = el.height / ((endY - startY) / ratioPx2Inch.value)
+
+            options.w = originW / ratioPx2Inch.value
+            options.h = originH / ratioPx2Inch.value
+
+            options.sizing = {
+              type: 'crop',
+              x: startX / ratioPx2Inch.value * originW / ratioPx2Inch.value,
+              y: startY / ratioPx2Inch.value * originH / ratioPx2Inch.value,
+              w: (endX - startX) / ratioPx2Inch.value * originW / ratioPx2Inch.value,
+              h: (endY - startY) / ratioPx2Inch.value * originH / ratioPx2Inch.value,
+            }
+          }
+
+          pptxSlide.addImage(options)
+        }
+
+        else if (el.type === 'shape') {
+          if (el.special) {
+            const svgRef = document.querySelector(`.thumbnail-list .base-element-${el.id} svg`) as HTMLElement
+            if (svgRef.clientWidth < 1 || svgRef.clientHeight < 1) continue // 临时处理(导入PPTX文件带来的异常数据)
+            const base64SVG = svg2Base64(svgRef)
+
+            const options: pptxgen.ImageProps = {
+              data: base64SVG,
+              x: el.left / ratioPx2Inch.value,
+              y: el.top / ratioPx2Inch.value,
+              w: el.width / ratioPx2Inch.value,
+              h: el.height / ratioPx2Inch.value,
+            }
+            if (el.rotate) options.rotate = el.rotate
+            if (el.link) {
+              const linkOption = getLinkOption(el.link)
+              if (linkOption) options.hyperlink = linkOption
+            }
+
+            pptxSlide.addImage(options)
+          }
+          else {
+            const scale = {
+              x: el.width / el.viewBox[0],
+              y: el.height / el.viewBox[1],
+            }
+            const points = formatPoints(toPoints(el.path), scale)
+  
+            let fillColor = formatColor(el.fill)
+            if (el.gradient) {
+              const colors = el.gradient.colors
+              const color1 = colors[0].color
+              const color2 = colors[colors.length - 1].color
+              const color = tinycolor.mix(color1, color2).toHexString()
+              fillColor = formatColor(color)
+            }
+            const opacity = el.opacity === undefined ? 1 : el.opacity
+  
+            const options: pptxgen.ShapeProps = {
+              x: el.left / ratioPx2Inch.value,
+              y: el.top / ratioPx2Inch.value,
+              w: el.width / ratioPx2Inch.value,
+              h: el.height / ratioPx2Inch.value,
+              fill: { color: fillColor.color, transparency: (1 - fillColor.alpha * opacity) * 100 },
+              points,
+            }
+            if (el.flipH) options.flipH = el.flipH
+            if (el.flipV) options.flipV = el.flipV
+            if (el.shadow) options.shadow = getShadowOption(el.shadow)
+            if (el.outline?.width) options.line = getOutlineOption(el.outline)
+            if (el.rotate) options.rotate = el.rotate
+            if (el.link) {
+              const linkOption = getLinkOption(el.link)
+              if (linkOption) options.hyperlink = linkOption
+            }
+
+            pptxSlide.addShape('custGeom' as pptxgen.ShapeType, options)
+          }
+          if (el.text) {
+            const textProps = formatHTML(el.text.content)
+
+            const options: pptxgen.TextPropsOptions = {
+              x: el.left / ratioPx2Inch.value,
+              y: el.top / ratioPx2Inch.value,
+              w: el.width / ratioPx2Inch.value,
+              h: el.height / ratioPx2Inch.value,
+              fontSize: 20 / ratioPx2Pt.value,
+              fontFace: '微软雅黑',
+              color: '#000000',
+              paraSpaceBefore: 5 / ratioPx2Pt.value,
+              valign: el.text.align,
+            }
+            if (el.rotate) options.rotate = el.rotate
+            if (el.text.defaultColor) options.color = formatColor(el.text.defaultColor).color
+            if (el.text.defaultFontName) options.fontFace = el.text.defaultFontName
+
+            pptxSlide.addText(textProps, options)
+          }
+        }
+
+        else if (el.type === 'line') {
+          const path = getLineElementPath(el)
+          const points = formatPoints(toPoints(path))
+          const { minX, maxX, minY, maxY } = getElementRange(el)
+          const c = formatColor(el.color)
+
+          const options: pptxgen.ShapeProps = {
+            x: el.left / ratioPx2Inch.value,
+            y: el.top / ratioPx2Inch.value,
+            w: (maxX - minX) / ratioPx2Inch.value,
+            h: (maxY - minY) / ratioPx2Inch.value,
+            line: {
+              color: c.color, 
+              transparency: (1 - c.alpha) * 100,
+              width: el.width / ratioPx2Pt.value, 
+              dashType: dashTypeMap[el.style] as 'solid' | 'dash' | 'sysDot',
+              beginArrowType: el.points[0] ? 'arrow' : 'none',
+              endArrowType: el.points[1] ? 'arrow' : 'none',
+            },
+            points,
+          }
+          if (el.shadow) options.shadow = getShadowOption(el.shadow)
+
+          pptxSlide.addShape('custGeom' as pptxgen.ShapeType, options)
+        }
+
+        else if (el.type === 'chart') {
+          const chartData = []
+          for (let i = 0; i < el.data.series.length; i++) {
+            const item = el.data.series[i]
+            chartData.push({
+              name: `系列${i + 1}`,
+              labels: el.data.labels,
+              values: item,
+            })
+          }
+
+          let chartColors: string[] = []
+          if (el.themeColors.length === 10) chartColors = el.themeColors.map(color => formatColor(color).color)
+          else if (el.themeColors.length === 1) chartColors = tinycolor(el.themeColors[0]).analogous(10).map(color => formatColor(color.toHexString()).color)
+          else {
+            const len = el.themeColors.length
+            const supplement = tinycolor(el.themeColors[len - 1]).analogous(10 + 1 - len).map(color => color.toHexString())
+            chartColors = [...el.themeColors.slice(0, len - 1), ...supplement].map(color => formatColor(color).color)
+          }
+          
+          const options: pptxgen.IChartOpts = {
+            x: el.left / ratioPx2Inch.value,
+            y: el.top / ratioPx2Inch.value,
+            w: el.width / ratioPx2Inch.value,
+            h: el.height / ratioPx2Inch.value,
+            chartColors: (el.chartType === 'pie' || el.chartType === 'ring') ? chartColors : chartColors.slice(0, el.data.series.length),
+          }
+
+          const textColor = formatColor(el.textColor || '#000000').color
+          options.catAxisLabelColor = textColor
+          options.valAxisLabelColor = textColor
+
+          const fontSize = 14 / ratioPx2Pt.value
+          options.catAxisLabelFontSize = fontSize
+          options.valAxisLabelFontSize = fontSize
+          
+          if (el.fill || el.outline) {
+            const plotArea: pptxgen.IChartPropsFillLine = {}
+            if (el.fill) {
+              plotArea.fill = { color: formatColor(el.fill).color }
+            }
+            if (el.outline) {
+              plotArea.border = {
+                pt: el.outline.width! / ratioPx2Pt.value,
+                color: formatColor(el.outline.color!).color,
+              }
+            }
+            options.plotArea = plotArea
+          }
+
+          if ((el.data.series.length > 1 && el.chartType !== 'scatter') || el.chartType === 'pie' || el.chartType === 'ring') {
+            options.showLegend = true
+            options.legendPos = 'b'
+            options.legendColor = textColor
+            options.legendFontSize = fontSize
+          }
+
+          let type = pptx.ChartType.bar
+          if (el.chartType === 'bar') {
+            type = pptx.ChartType.bar
+            options.barDir = 'col'
+            if (el.options?.stack) options.barGrouping = 'stacked'
+          }
+          else if (el.chartType === 'column') {
+            type = pptx.ChartType.bar
+            options.barDir = 'bar'
+            if (el.options?.stack) options.barGrouping = 'stacked'
+          }
+          else if (el.chartType === 'line') {
+            type = pptx.ChartType.line
+            if (el.options?.lineSmooth) options.lineSmooth = true
+          }
+          else if (el.chartType === 'area') {
+            type = pptx.ChartType.area
+          }
+          else if (el.chartType === 'radar') {
+            type = pptx.ChartType.radar
+          }
+          else if (el.chartType === 'scatter') {
+            type = pptx.ChartType.scatter
+            options.lineSize = 0
+          }
+          else if (el.chartType === 'pie') {
+            type = pptx.ChartType.pie
+          }
+          else if (el.chartType === 'ring') {
+            type = pptx.ChartType.doughnut
+            options.holeSize = 60
+          }
+          
+          pptxSlide.addChart(type, chartData, options)
+        }
+
+        else if (el.type === 'table') {
+          const hiddenCells = []
+          for (let i = 0; i < el.data.length; i++) {
+            const rowData = el.data[i]
+
+            for (let j = 0; j < rowData.length; j++) {
+              const cell = rowData[j]
+              if (cell.colspan > 1 || cell.rowspan > 1) {
+                for (let row = i; row < i + cell.rowspan; row++) {
+                  for (let col = row === i ? j + 1 : j; col < j + cell.colspan; col++) hiddenCells.push(`${row}_${col}`)
+                }
+              }
+            }
+          }
+
+          const tableData = []
+
+          const theme = el.theme
+          let themeColor: FormatColor | null = null
+          let subThemeColors: FormatColor[] = []
+          if (theme) {
+            themeColor = formatColor(theme.color)
+            subThemeColors = getTableSubThemeColor(theme.color).map(item => formatColor(item))
+          }
+
+          for (let i = 0; i < el.data.length; i++) {
+            const row = el.data[i]
+            const _row = []
+
+            for (let j = 0; j < row.length; j++) {
+              const cell = row[j]
+              const cellOptions: pptxgen.TableCellProps = {
+                colspan: cell.colspan,
+                rowspan: cell.rowspan,
+                bold: cell.style?.bold || false,
+                italic: cell.style?.em || false,
+                underline: { style: cell.style?.underline ? 'sng' : 'none' },
+                align: cell.style?.align || 'left',
+                valign: 'middle',
+                fontFace: cell.style?.fontname || '微软雅黑',
+                fontSize: (cell.style?.fontsize ? parseInt(cell.style?.fontsize) : 14) / ratioPx2Pt.value,
+              }
+              if (theme && themeColor) {
+                let c: FormatColor
+                if (i % 2 === 0) c = subThemeColors[1]
+                else c = subThemeColors[0]
+
+                if (theme.rowHeader && i === 0) c = themeColor
+                else if (theme.rowFooter && i === el.data.length - 1) c = themeColor
+                else if (theme.colHeader && j === 0) c = themeColor
+                else if (theme.colFooter && j === row.length - 1) c = themeColor
+
+                cellOptions.fill = { color: c.color, transparency: (1 - c.alpha) * 100 }
+              }
+              if (cell.style?.backcolor) {
+                const c = formatColor(cell.style.backcolor)
+                cellOptions.fill = { color: c.color, transparency: (1 - c.alpha) * 100 }
+              }
+              if (cell.style?.color) cellOptions.color = formatColor(cell.style.color).color
+
+              if (!hiddenCells.includes(`${i}_${j}`)) {
+                _row.push({
+                  text: cell.text,
+                  options: cellOptions,
+                })
+              }
+            }
+            if (_row.length) tableData.push(_row)
+          }
+
+          const options: pptxgen.TableProps = {
+            x: el.left / ratioPx2Inch.value,
+            y: el.top / ratioPx2Inch.value,
+            w: el.width / ratioPx2Inch.value,
+            h: el.height / ratioPx2Inch.value,
+            colW: el.colWidths.map(item => el.width * item / ratioPx2Inch.value),
+          }
+          if (el.theme) options.fill = { color: '#ffffff' }
+          if (el.outline.width && el.outline.color) {
+            options.border = {
+              type: el.outline.style === 'solid' ? 'solid' : 'dash',
+              pt: el.outline.width / ratioPx2Pt.value,
+              color: formatColor(el.outline.color).color,
+            }
+          }
+
+          pptxSlide.addTable(tableData, options)
+        }
+        
+        else if (el.type === 'latex') {
+          const svgRef = document.querySelector(`.thumbnail-list .base-element-${el.id} svg`) as HTMLElement
+          const base64SVG = svg2Base64(svgRef)
+
+          const options: pptxgen.ImageProps = {
+            data: base64SVG,
+            x: el.left / ratioPx2Inch.value,
+            y: el.top / ratioPx2Inch.value,
+            w: el.width / ratioPx2Inch.value,
+            h: el.height / ratioPx2Inch.value,
+          }
+          if (el.link) {
+            const linkOption = getLinkOption(el.link)
+            if (linkOption) options.hyperlink = linkOption
+          }
+
+          pptxSlide.addImage(options)
+        }
+        
+        else if (!ignoreMedia && (el.type === 'video' || el.type === 'audio')) {
+          const options: pptxgen.MediaProps = {
+            x: el.left / ratioPx2Inch.value,
+            y: el.top / ratioPx2Inch.value,
+            w: el.width / ratioPx2Inch.value,
+            h: el.height / ratioPx2Inch.value,
+            path: el.src,
+            type: el.type,
+          }
+          if (el.type === 'video' && el.poster) options.cover = el.poster
+
+          const extMatch = el.src.match(/\.([a-zA-Z0-9]+)(?:[\?#]|$)/)
+          if (extMatch && extMatch[1]) options.extn = extMatch[1]
+          else if (el.ext) options.extn = el.ext
+          
+          const videoExts = ['avi', 'mp4', 'm4v', 'mov', 'wmv']
+          const audioExts = ['mp3', 'm4a', 'mp4', 'wav', 'wma']
+          if (options.extn && [...videoExts, ...audioExts].includes(options.extn)) {
+            pptxSlide.addMedia(options)
+          }
+        }
+      }
+    }
+
+    setTimeout(() => {
+      pptx.writeFile({ fileName: `${title.value}.pptx` }).then(() => exporting.value = false).catch(() => {
+        exporting.value = false
+        message.error('导出失败')
+      })
+    }, 200)
+  }
+
+  return {
+    exporting,
+    exportImage,
+    exportJSON,
+    exportSpecificFile,
+    exportPPTX,
+  }
+}

+ 320 - 0
src/hooks/useGlobalHotkey.ts

@@ -0,0 +1,320 @@
+import { onMounted, onUnmounted } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useMainStore, useSlidesStore, useKeyboardStore } from '@/store'
+import { ElementOrderCommands } from '@/types/edit'
+import { KEYS } from '@/configs/hotkey'
+
+import useSlideHandler from './useSlideHandler'
+import useLockElement from './useLockElement'
+import useDeleteElement from './useDeleteElement'
+import useCombineElement from './useCombineElement'
+import useCopyAndPasteElement from './useCopyAndPasteElement'
+import useSelectElement from './useSelectElement'
+import useMoveElement from './useMoveElement'
+import useOrderElement from './useOrderElement'
+import useHistorySnapshot from './useHistorySnapshot'
+import useScreening from './useScreening'
+import useScaleCanvas from './useScaleCanvas'
+
+export default () => {
+  const mainStore = useMainStore()
+  const keyboardStore = useKeyboardStore()
+  const {
+    activeElementIdList,
+    disableHotkeys,
+    handleElement,
+    handleElementId,
+    editorAreaFocus,
+    thumbnailsFocus,
+    showSearchPanel,
+  } = storeToRefs(mainStore)
+  const { currentSlide } = storeToRefs(useSlidesStore())
+  const { ctrlKeyState, shiftKeyState, spaceKeyState } = storeToRefs(keyboardStore)
+
+  const {
+    updateSlideIndex,
+    copySlide,
+    createSlide,
+    deleteSlide,
+    cutSlide,
+    copyAndPasteSlide,
+    selectAllSlide,
+  } = useSlideHandler()
+
+  const { combineElements, uncombineElements } = useCombineElement()
+  const { deleteElement } = useDeleteElement()
+  const { lockElement } = useLockElement()
+  const { copyElement, cutElement, quickCopyElement } = useCopyAndPasteElement()
+  const { selectAllElements } = useSelectElement()
+  const { moveElement } = useMoveElement()
+  const { orderElement } = useOrderElement()
+  const { redo, undo } = useHistorySnapshot()
+  const { enterScreening, enterScreeningFromStart } = useScreening()
+  const { scaleCanvas, resetCanvas } = useScaleCanvas()
+
+  const copy = () => {
+    if (activeElementIdList.value.length) copyElement()
+    else if (thumbnailsFocus.value) copySlide()
+  }
+
+  const cut = () => {
+    if (activeElementIdList.value.length) cutElement()
+    else if (thumbnailsFocus.value) cutSlide()
+  }
+
+  const quickCopy = () => {
+    if (activeElementIdList.value.length) quickCopyElement()
+    else if (thumbnailsFocus.value) copyAndPasteSlide()
+  }
+
+  const selectAll = () => {
+    if (editorAreaFocus.value) selectAllElements()
+    if (thumbnailsFocus.value) selectAllSlide()
+  }
+
+  const lock = () => {
+    if (!editorAreaFocus.value) return
+    lockElement()
+  }
+  const combine = () => {
+    if (!editorAreaFocus.value) return
+    combineElements()
+  }
+
+  const uncombine = () => {
+    if (!editorAreaFocus.value) return
+    uncombineElements()
+  }
+
+  const remove = () => {
+    if (activeElementIdList.value.length) deleteElement()
+    else if (thumbnailsFocus.value) deleteSlide()
+  }
+
+  const move = (key: string) => {
+    if (activeElementIdList.value.length) moveElement(key)
+    else if (key === KEYS.UP || key === KEYS.DOWN) updateSlideIndex(key)
+  }
+
+  const moveSlide = (key: string) => {
+    if (key === KEYS.PAGEUP) updateSlideIndex(KEYS.UP)
+    else if (key === KEYS.PAGEDOWN) updateSlideIndex(KEYS.DOWN)
+  }
+
+  const order = (command: ElementOrderCommands) => {
+    if (!handleElement.value) return
+    orderElement(handleElement.value, command)
+  }
+
+  const create = () => {
+    if (!thumbnailsFocus.value) return
+    createSlide()
+  }
+
+  const tabActiveElement = () => {
+    if (!currentSlide.value.elements.length) return
+    if (!handleElementId.value) {
+      const firstElement = currentSlide.value.elements[0]
+      mainStore.setActiveElementIdList([firstElement.id])
+      return
+    }
+    const currentIndex = currentSlide.value.elements.findIndex(el => el.id === handleElementId.value)
+    const nextIndex = currentIndex >= currentSlide.value.elements.length - 1 ? 0 : currentIndex + 1
+    const nextElementId = currentSlide.value.elements[nextIndex].id
+
+    mainStore.setActiveElementIdList([nextElementId])
+  }
+
+  const keydownListener = (e: KeyboardEvent) => {
+    const { ctrlKey, shiftKey, altKey, metaKey } = e
+    const ctrlOrMetaKeyActive = ctrlKey || metaKey
+    
+    const key = e.key.toUpperCase()
+
+    if (ctrlOrMetaKeyActive && !ctrlKeyState.value) keyboardStore.setCtrlKeyState(true)
+    if (shiftKey && !shiftKeyState.value) keyboardStore.setShiftKeyState(true)
+    if (!disableHotkeys.value && key === KEYS.SPACE) keyboardStore.setSpaceKeyState(true)
+
+    
+    if (ctrlOrMetaKeyActive && key === KEYS.P) {
+      e.preventDefault()
+      mainStore.setDialogForExport('pdf')
+      return
+    }
+    if (shiftKey && key === KEYS.F5) {
+      e.preventDefault()
+      enterScreening()
+      keyboardStore.setShiftKeyState(false)
+      return
+    }
+    if (key === KEYS.F5) {
+      e.preventDefault()
+      enterScreeningFromStart()
+      return
+    }
+    if (ctrlKey && key === KEYS.F) {
+      e.preventDefault()
+      mainStore.setSearchPanelState(!showSearchPanel.value)
+      return
+    }
+    
+    if (!editorAreaFocus.value && !thumbnailsFocus.value) return      
+
+    if (ctrlOrMetaKeyActive && key === KEYS.C) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      copy()
+    }
+    if (ctrlOrMetaKeyActive && key === KEYS.X) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      cut()
+    }
+    if (ctrlOrMetaKeyActive && key === KEYS.D) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      quickCopy()
+    }
+    if (ctrlOrMetaKeyActive && key === KEYS.Z) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      undo()
+    }
+    if (ctrlOrMetaKeyActive && key === KEYS.Y) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      redo()
+    }
+    if (ctrlOrMetaKeyActive && key === KEYS.A) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      selectAll()
+    }
+    if (ctrlOrMetaKeyActive && key === KEYS.L) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      lock()
+    }
+    if (!shiftKey && ctrlOrMetaKeyActive && key === KEYS.G) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      combine()
+    }
+    if (shiftKey && ctrlOrMetaKeyActive && key === KEYS.G) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      uncombine()
+    }
+    if (altKey && key === KEYS.F) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      order(ElementOrderCommands.TOP)
+    }
+    if (altKey && key === KEYS.B) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      order(ElementOrderCommands.BOTTOM)
+    }
+    if (key === KEYS.DELETE || key === KEYS.BACKSPACE) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      remove()
+    }
+    if (key === KEYS.UP) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      move(KEYS.UP)
+    }
+    if (key === KEYS.DOWN) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      move(KEYS.DOWN)
+    }
+    if (key === KEYS.LEFT) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      move(KEYS.LEFT)
+    }
+    if (key === KEYS.RIGHT) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      move(KEYS.RIGHT)
+    }
+    if (key === KEYS.PAGEUP) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      moveSlide(KEYS.PAGEUP)
+    }
+    if (key === KEYS.PAGEDOWN) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      moveSlide(KEYS.PAGEDOWN)
+    }
+    if (key === KEYS.ENTER) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      create()
+    }
+    if (key === KEYS.MINUS) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      scaleCanvas('-')
+    }
+    if (key === KEYS.EQUAL) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      scaleCanvas('+')
+    }
+    if (key === KEYS.DIGIT_0) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      resetCanvas()
+    }
+    if (key === KEYS.TAB) {
+      if (disableHotkeys.value) return
+      e.preventDefault()
+      tabActiveElement()
+    }
+    if (editorAreaFocus.value && !shiftKey && !ctrlOrMetaKeyActive && !disableHotkeys.value) {
+      if (key === KEYS.T) {
+        mainStore.setCreatingElement({ type: 'text' })
+      }
+      else if (key === KEYS.R) {
+        mainStore.setCreatingElement({ type: 'shape', data: {
+          viewBox: [200, 200],
+          path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
+        }})
+      }
+      else if (key === KEYS.O) {
+        mainStore.setCreatingElement({ type: 'shape', data: {
+          viewBox: [200, 200],
+          path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z',
+        }})
+      }
+      else if (key === KEYS.L) {
+        mainStore.setCreatingElement({ type: 'line', data: {
+          path: 'M 0 0 L 20 20',
+          style: 'solid',
+          points: ['', ''],
+        }})
+      }
+    }
+  }
+  
+  const keyupListener = () => {
+    if (ctrlKeyState.value) keyboardStore.setCtrlKeyState(false)
+    if (shiftKeyState.value) keyboardStore.setShiftKeyState(false)
+    if (spaceKeyState.value) keyboardStore.setSpaceKeyState(false)
+  }
+
+  onMounted(() => {
+    document.addEventListener('keydown', keydownListener)
+    document.addEventListener('keyup', keyupListener)
+    window.addEventListener('blur', keyupListener)
+  })
+  onUnmounted(() => {
+    document.removeEventListener('keydown', keydownListener)
+    document.removeEventListener('keyup', keyupListener)
+    window.removeEventListener('blur', keyupListener)
+  })
+}

+ 35 - 0
src/hooks/useHideElement.ts

@@ -0,0 +1,35 @@
+import { storeToRefs } from 'pinia'
+import { useSlidesStore, useMainStore } from '@/store'
+
+export default () => {
+  const slidesStore = useSlidesStore()
+  const mainStore = useMainStore()
+  const { currentSlide } = storeToRefs(slidesStore)
+  const { activeElementIdList, hiddenElementIdList } = storeToRefs(mainStore)
+
+  const toggleHideElement = (id: string) => {
+    if (hiddenElementIdList.value.includes(id)) {
+      mainStore.setHiddenElementIdList(hiddenElementIdList.value.filter(item => item !== id))
+    }
+    else mainStore.setHiddenElementIdList([...hiddenElementIdList.value, id])
+  
+    if (activeElementIdList.value.includes(id)) mainStore.setActiveElementIdList([])
+  }
+  
+  const showAllElements = () => {
+    const currentSlideElIdList = currentSlide.value.elements.map(item => item.id)
+    const needHiddenElementIdList = hiddenElementIdList.value.filter(item => !currentSlideElIdList.includes(item))
+    mainStore.setHiddenElementIdList(needHiddenElementIdList)
+  }
+  const hideAllElements = () => {
+    const currentSlideElIdList = currentSlide.value.elements.map(item => item.id)
+    mainStore.setHiddenElementIdList([...hiddenElementIdList.value, ...currentSlideElIdList])
+    if (activeElementIdList.value.length) mainStore.setActiveElementIdList([])
+  }
+
+  return {
+    toggleHideElement,
+    showAllElements,
+    hideAllElements,
+  }
+}

+ 27 - 0
src/hooks/useHistorySnapshot.ts

@@ -0,0 +1,27 @@
+import { debounce, throttle} from 'lodash'
+import { useSnapshotStore } from '@/store'
+
+export default () => {
+  const snapshotStore = useSnapshotStore()
+
+  // 添加历史快照(历史记录)
+  const addHistorySnapshot = debounce(function() {
+    snapshotStore.addSnapshot()
+  }, 300, { trailing: true })
+
+  // 重做
+  const redo = throttle(function() {
+    snapshotStore.reDo()
+  }, 100, { leading: true, trailing: false })
+
+  // 撤销
+  const undo = throttle(function() {
+    snapshotStore.unDo()
+  }, 100, { leading: true, trailing: false })
+
+  return {
+    addHistorySnapshot,
+    redo,
+    undo,
+  }
+}

+ 494 - 0
src/hooks/useImport.ts

@@ -0,0 +1,494 @@
+import { ref } from 'vue'
+import { storeToRefs } from 'pinia'
+import { parse, type Shape, type Element, type ChartItem } from 'pptxtojson'
+import { nanoid } from 'nanoid'
+import { useSlidesStore } from '@/store'
+import { decrypt } from '@/utils/crypto'
+import { type ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from '@/configs/shapes'
+import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
+import useSlideHandler from '@/hooks/useSlideHandler'
+import message from '@/utils/message'
+import { getSvgPathRange } from '@/utils/svgPathParser'
+import type {
+  Slide,
+  TableCellStyle,
+  TableCell,
+  ChartType,
+  SlideBackground,
+  PPTShapeElement,
+  PPTLineElement,
+  ShapeTextAlign,
+  PPTTextElement,
+  ChartOptions,
+} from '@/types/slides'
+
+const convertFontSizePtToPx = (html: string, ratio: number) => {
+  return html.replace(/font-size:\s*([\d.]+)pt/g, (match, p1) => {
+    return `font-size: ${(parseFloat(p1) * ratio).toFixed(1)}px`
+  })
+}
+
+export default () => {
+  const slidesStore = useSlidesStore()
+  const { theme } = storeToRefs(useSlidesStore())
+
+  const { addSlidesFromData } = useAddSlidesOrElements()
+  const { isEmptySlide } = useSlideHandler()
+
+  const exporting = ref(false)
+
+  // 导入pptist文件
+  const importSpecificFile = (files: FileList, cover = false) => {
+    const file = files[0]
+
+    const reader = new FileReader()
+    reader.addEventListener('load', () => {
+      try {
+        const slides = JSON.parse(decrypt(reader.result as string))
+        if (cover) {
+          slidesStore.updateSlideIndex(0)
+          slidesStore.setSlides(slides)
+        }
+        else if (isEmptySlide.value) slidesStore.setSlides(slides)
+        else addSlidesFromData(slides)
+      }
+      catch {
+        message.error('无法正确读取 / 解析该文件')
+      }
+    })
+    reader.readAsText(file)
+  }
+
+  const parseLineElement = (el: Shape) => {
+    let start: [number, number] = [0, 0]
+    let end: [number, number] = [0, 0]
+
+    if (!el.isFlipV && !el.isFlipH) { // 右下
+      start = [0, 0]
+      end = [el.width, el.height]
+    }
+    else if (el.isFlipV && el.isFlipH) { // 左上
+      start = [el.width, el.height]
+      end = [0, 0]
+    }
+    else if (el.isFlipV && !el.isFlipH) { // 右上
+      start = [0, el.height]
+      end = [el.width, 0]
+    }
+    else { // 左下
+      start = [el.width, 0]
+      end = [0, el.height]
+    }
+
+    const data: PPTLineElement = {
+      type: 'line',
+      id: nanoid(10),
+      width: el.borderWidth || 1,
+      left: el.left,
+      top: el.top,
+      start,
+      end,
+      style: el.borderType,
+      color: el.borderColor,
+      points: ['', /straightConnector/.test(el.shapType) ? 'arrow' : '']
+    }
+    if (/bentConnector/.test(el.shapType)) {
+      data.broken2 = [
+        Math.abs(start[0] - end[0]) / 2,
+        Math.abs(start[1] - end[1]) / 2,
+      ]
+    }
+
+    return data
+  }
+
+  // 导入PPTX文件
+  const importPPTXFile = (files: FileList) => {
+    const file = files[0]
+    if (!file) return
+
+    exporting.value = true
+
+    const shapeList: ShapePoolItem[] = []
+    for (const item of SHAPE_LIST) {
+      shapeList.push(...item.children)
+    }
+    
+    const reader = new FileReader()
+    reader.onload = async e => {
+      const json = await parse(e.target!.result as ArrayBuffer)
+
+      const ratio = 96 / 72
+      const width = json.size.width
+
+      slidesStore.setViewportSize(width * ratio)
+
+      const slides: Slide[] = []
+      for (const item of json.slides) {
+        const { type, value } = item.fill
+        let background: SlideBackground
+        if (type === 'image') {
+          background = {
+            type: 'image',
+            image: {
+              src: value.picBase64,
+              size: 'cover',
+            },
+          }
+        }
+        else if (type === 'gradient') {
+          background = {
+            type: 'gradient',
+            gradient: {
+              type: 'linear',
+              colors: value.colors.map(item => ({
+                ...item,
+                pos: parseInt(item.pos),
+              })),
+              rotate: value.rot,
+            },
+          }
+        }
+        else {
+          background = {
+            type: 'solid',
+            color: value,
+          }
+        }
+
+        const slide: Slide = {
+          id: nanoid(10),
+          elements: [],
+          background,
+        }
+
+        const parseElements = (elements: Element[]) => {
+          for (const el of elements) {
+            const originWidth = el.width || 1
+            const originHeight = el.height || 1
+            const originLeft = el.left
+            const originTop = el.top
+
+            el.width = el.width * ratio
+            el.height = el.height * ratio
+            el.left = el.left * ratio
+            el.top = el.top * ratio
+  
+            if (el.type === 'text') {
+              const textEl: PPTTextElement = {
+                type: 'text',
+                id: nanoid(10),
+                width: el.width,
+                height: el.height,
+                left: el.left,
+                top: el.top,
+                rotate: el.rotate,
+                defaultFontName: theme.value.fontName,
+                defaultColor: theme.value.fontColor,
+                content: convertFontSizePtToPx(el.content, ratio),
+                lineHeight: 1,
+                outline: {
+                  color: el.borderColor,
+                  width: el.borderWidth,
+                  style: el.borderType,
+                },
+                fill: el.fillColor,
+                vertical: el.isVertical,
+              }
+              if (el.shadow) {
+                textEl.shadow = {
+                  h: el.shadow.h * ratio,
+                  v: el.shadow.v * ratio,
+                  blur: el.shadow.blur * ratio,
+                  color: el.shadow.color,
+                }
+              }
+              slide.elements.push(textEl)
+            }
+            else if (el.type === 'image') {
+              slide.elements.push({
+                type: 'image',
+                id: nanoid(10),
+                src: el.src,
+                width: el.width,
+                height: el.height,
+                left: el.left,
+                top: el.top,
+                fixedRatio: true,
+                rotate: el.rotate,
+                flipH: el.isFlipH,
+                flipV: el.isFlipV,
+              })
+            }
+            else if (el.type === 'audio') {
+              slide.elements.push({
+                type: 'audio',
+                id: nanoid(10),
+                src: el.blob,
+                width: el.width,
+                height: el.height,
+                left: el.left,
+                top: el.top,
+                rotate: 0,
+                fixedRatio: false,
+                color: theme.value.themeColor,
+                loop: false,
+                autoplay: false,
+              })
+            }
+            else if (el.type === 'video') {
+              slide.elements.push({
+                type: 'video',
+                id: nanoid(10),
+                src: (el.blob || el.src)!,
+                width: el.width,
+                height: el.height,
+                left: el.left,
+                top: el.top,
+                rotate: 0,
+                autoplay: false,
+              })
+            }
+            else if (el.type === 'shape') {
+              if (el.shapType === 'line' || /Connector/.test(el.shapType)) {
+                const lineElement = parseLineElement(el)
+                slide.elements.push(lineElement)
+              }
+              else {
+                const shape = shapeList.find(item => item.pptxShapeType === el.shapType)
+
+                const vAlignMap: { [key: string]: ShapeTextAlign } = {
+                  'mid': 'middle',
+                  'down': 'bottom',
+                  'up': 'top',
+                }
+                
+                const element: PPTShapeElement = {
+                  type: 'shape',
+                  id: nanoid(10),
+                  width: el.width,
+                  height: el.height,
+                  left: el.left,
+                  top: el.top,
+                  viewBox: [200, 200],
+                  path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
+                  fill: el.fillColor || 'none',
+                  fixedRatio: false,
+                  rotate: el.rotate,
+                  outline: {
+                    color: el.borderColor,
+                    width: el.borderWidth,
+                    style: el.borderType,
+                  },
+                  text: {
+                    content: convertFontSizePtToPx(el.content, ratio),
+                    defaultFontName: theme.value.fontName,
+                    defaultColor: theme.value.fontColor,
+                    align: vAlignMap[el.vAlign] || 'middle',
+                  },
+                  flipH: el.isFlipH,
+                  flipV: el.isFlipV,
+                }
+                if (el.shadow) {
+                  element.shadow = {
+                    h: el.shadow.h * ratio,
+                    v: el.shadow.v * ratio,
+                    blur: el.shadow.blur * ratio,
+                    color: el.shadow.color,
+                  }
+                }
+    
+                if (shape) {
+                  element.path = shape.path
+                  element.viewBox = shape.viewBox
+    
+                  if (shape.pathFormula) {
+                    element.pathFormula = shape.pathFormula
+                    element.viewBox = [el.width, el.height]
+    
+                    const pathFormula = SHAPE_PATH_FORMULAS[shape.pathFormula]
+                    if ('editable' in pathFormula && pathFormula.editable) {
+                      element.path = pathFormula.formula(el.width, el.height, pathFormula.defaultValue)
+                      element.keypoints = pathFormula.defaultValue
+                    }
+                    else element.path = pathFormula.formula(el.width, el.height)
+                  }
+                }
+                if (el.shapType === 'custom') {
+                  if (el.path!.indexOf('NaN') !== -1) element.path = ''
+                  else {
+                    element.special = true
+                    element.path = el.path!
+  
+                    const { maxX, maxY } = getSvgPathRange(element.path)
+                    element.viewBox = [maxX || originWidth, maxY || originHeight]
+                  }
+                }
+    
+                if (element.path) slide.elements.push(element)
+              }
+            }
+            else if (el.type === 'table') {
+              const row = el.data.length
+              const col = el.data[0].length
+  
+              const style: TableCellStyle = {
+                fontname: theme.value.fontName,
+                color: theme.value.fontColor,
+              }
+              const data: TableCell[][] = []
+              for (let i = 0; i < row; i++) {
+                const rowCells: TableCell[] = []
+                for (let j = 0; j < col; j++) {
+                  const cellData = el.data[i][j]
+
+                  let textDiv: HTMLDivElement | null = document.createElement('div')
+                  textDiv.innerHTML = cellData.text
+                  const p = textDiv.querySelector('p')
+                  const align = p?.style.textAlign || 'left'
+
+                  const span = textDiv.querySelector('span')
+                  const fontsize = span?.style.fontSize ? (parseInt(span?.style.fontSize) * ratio).toFixed(1) + 'px' : ''
+                  const fontname = span?.style.fontFamily || ''
+                  const color = span?.style.color || cellData.fontColor
+
+                  rowCells.push({
+                    id: nanoid(10),
+                    colspan: cellData.colSpan || 1,
+                    rowspan: cellData.rowSpan || 1,
+                    text: textDiv.innerText,
+                    style: {
+                      ...style,
+                      align: ['left', 'right', 'center'].includes(align) ? (align as 'left' | 'right' | 'center') : 'left',
+                      fontsize,
+                      fontname,
+                      color,
+                      bold: cellData.fontBold,
+                      backcolor: cellData.fillColor,
+                    },
+                  })
+                  textDiv = null
+                }
+                data.push(rowCells)
+              }
+  
+              const colWidths: number[] = new Array(col).fill(1 / col)
+  
+              slide.elements.push({
+                type: 'table',
+                id: nanoid(10),
+                width: el.width,
+                height: el.height,
+                left: el.left,
+                top: el.top,
+                colWidths,
+                rotate: 0,
+                data,
+                outline: {
+                  width: el.borderWidth || 2,
+                  style: el.borderType,
+                  color: el.borderColor || '#eeece1',
+                },
+                cellMinHeight: 36,
+              })
+            }
+            else if (el.type === 'chart') {
+              let labels: string[]
+              let legends: string[]
+              let series: number[][]
+  
+              if (el.chartType === 'scatterChart' || el.chartType === 'bubbleChart') {
+                labels = el.data[0].map((item, index) => `坐标${index + 1}`)
+                legends = ['X', 'Y']
+                series = el.data
+              }
+              else {
+                const data = el.data as ChartItem[]
+                labels = Object.values(data[0].xlabels)
+                legends = data.map(item => item.key)
+                series = data.map(item => item.values.map(v => v.y))
+              }
+
+              const options: ChartOptions = {}
+  
+              let chartType: ChartType = 'bar'
+
+              switch (el.chartType) {
+                case 'barChart':
+                case 'bar3DChart':
+                  chartType = 'bar'
+                  if (el.barDir === 'bar') chartType = 'column'
+                  if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
+                  break
+                case 'lineChart':
+                case 'line3DChart':
+                  if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
+                  chartType = 'line'
+                  break
+                case 'areaChart':
+                case 'area3DChart':
+                  if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true
+                  chartType = 'area'
+                  break
+                case 'scatterChart':
+                case 'bubbleChart':
+                  chartType = 'scatter'
+                  break
+                case 'pieChart':
+                case 'pie3DChart':
+                  chartType = 'pie'
+                  break
+                case 'radarChart':
+                  chartType = 'radar'
+                  break
+                case 'doughnutChart':
+                  chartType = 'ring'
+                  break
+                default:
+              }
+  
+              slide.elements.push({
+                type: 'chart',
+                id: nanoid(10),
+                chartType: chartType,
+                width: el.width,
+                height: el.height,
+                left: el.left,
+                top: el.top,
+                rotate: 0,
+                themeColors: [theme.value.themeColor],
+                textColor: theme.value.fontColor,
+                data: {
+                  labels,
+                  legends,
+                  series,
+                },
+                options,
+              })
+            }
+            else if (el.type === 'group' || el.type === 'diagram') {
+              const elements = el.elements.map(_el => ({
+                ..._el,
+                left: _el.left + originLeft,
+                top: _el.top + originTop,
+              }))
+              parseElements(elements)
+            }
+          }
+        }
+        parseElements(item.elements)
+        slides.push(slide)
+      }
+      slidesStore.updateSlideIndex(0)
+      slidesStore.setSlides(slides)
+      exporting.value = false
+    }
+    reader.readAsArrayBuffer(file)
+  }
+
+  return {
+    importSpecificFile,
+    importPPTXFile,
+    exporting,
+  }
+}

+ 37 - 0
src/hooks/useLink.ts

@@ -0,0 +1,37 @@
+import { useSlidesStore } from '@/store'
+import type { PPTElement, PPTElementLink } from '@/types/slides'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
+import message from '@/utils/message'
+
+export default () => {
+  const slidesStore = useSlidesStore()
+
+  const { addHistorySnapshot } = useHistorySnapshot()
+
+  const setLink = (handleElement: PPTElement, link: PPTElementLink) => {
+    const linkRegExp = /^(https?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/
+    if (link.type === 'web' && !linkRegExp.test(link.target)) {
+      message.error('不是正确的网页链接地址')
+      return false
+    }
+    if (link.type === 'slide' && !link.target) {
+      message.error('请先选择链接目标')
+      return false
+    }
+    const props = { link }
+    slidesStore.updateElement({ id: handleElement.id, props })
+    addHistorySnapshot()
+
+    return true
+  }
+
+  const removeLink = (handleElement: PPTElement) => {
+    slidesStore.removeElementProps({ id: handleElement.id, propName: 'link' })
+    addHistorySnapshot()
+  }
+
+  return {
+    setLink,
+    removeLink,
+  }
+}

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