jack hai 1 semana
achega
9b26c25e82
Modificáronse 100 ficheiros con 18967 adicións e 0 borrados
  1. 77 0
      .eslintrc.cjs
  2. 30 0
      .gitignore
  3. 661 0
      LICENSE
  4. 238 0
      README.md
  5. 228 0
      README_zh.md
  6. 16 0
      commitlint.config.cjs
  7. 61 0
      doc/AIPPT.md
  8. 5 0
      doc/Blacklist.md
  9. 67 0
      doc/Canvas.md
  10. 414 0
      doc/CustomElement.md
  11. 43 0
      doc/DirectoryAndData.md
  12. 157 0
      doc/Q&A.md
  13. 2 0
      env.d.ts
  14. 63 0
      index.html
  15. 9496 0
      package-lock.json
  16. 76 0
      package.json
  17. BIN=BIN
      public/favicon.ico
  18. BIN=BIN
      public/logo.png
  19. 475 0
      public/mocks/AIPPT.json
  20. 69 0
      public/mocks/AIPPT_Outline.md
  21. 482 0
      public/mocks/imgs.json
  22. 184 0
      public/mocks/slides.json
  23. 0 0
      public/mocks/template_1.json
  24. 63 0
      src/App.vue
  25. BIN=BIN
      src/assets/fonts/AlibabaPuHuiTi.woff2
  26. BIN=BIN
      src/assets/fonts/CangerXiaowanzi.woff2
  27. BIN=BIN
      src/assets/fonts/DeYiHei.woff2
  28. BIN=BIN
      src/assets/fonts/FangZhengFangSong.woff2
  29. BIN=BIN
      src/assets/fonts/FangZhengHeiTi.woff2
  30. BIN=BIN
      src/assets/fonts/FangZhengKaiTi.woff2
  31. BIN=BIN
      src/assets/fonts/FangZhengShuSong.woff2
  32. BIN=BIN
      src/assets/fonts/FengguangMingrui.woff2
  33. BIN=BIN
      src/assets/fonts/LXGWWenKai.woff2
  34. BIN=BIN
      src/assets/fonts/MiSans.woff2
  35. BIN=BIN
      src/assets/fonts/RuiziZhenyan.woff2
  36. BIN=BIN
      src/assets/fonts/ShetuModernSquare.woff2
  37. BIN=BIN
      src/assets/fonts/SourceHanSans.woff2
  38. BIN=BIN
      src/assets/fonts/SourceHanSerif.woff2
  39. BIN=BIN
      src/assets/fonts/SucaiJishiCoolSquare.woff2
  40. BIN=BIN
      src/assets/fonts/SucaiJishiKangkang.woff2
  41. BIN=BIN
      src/assets/fonts/TuniuRounded.woff2
  42. BIN=BIN
      src/assets/fonts/WenDingPLKaiTi.woff2
  43. BIN=BIN
      src/assets/fonts/YousheTitleBlack.woff2
  44. BIN=BIN
      src/assets/fonts/ZcoolHappy.woff2
  45. BIN=BIN
      src/assets/fonts/ZhuQueFangSong.woff2
  46. BIN=BIN
      src/assets/fonts/ZizhiQuXiMai.woff2
  47. 9 0
      src/assets/styles/font.scss
  48. 138 0
      src/assets/styles/global.scss
  49. 42 0
      src/assets/styles/mixin.scss
  50. 102 0
      src/assets/styles/prosemirror.scss
  51. 13 0
      src/assets/styles/variable.scss
  52. 7 0
      src/components.d.ts
  53. 116 0
      src/components/Button.vue
  54. 86 0
      src/components/ButtonGroup.vue
  55. 109 0
      src/components/Checkbox.vue
  56. 21 0
      src/components/CheckboxButton.vue
  57. 42 0
      src/components/ColorButton.vue
  58. 58 0
      src/components/ColorListButton.vue
  59. 107 0
      src/components/ColorPicker/Alpha.vue
  60. 60 0
      src/components/ColorPicker/Checkboard.vue
  61. 69 0
      src/components/ColorPicker/EditableInput.vue
  62. 117 0
      src/components/ColorPicker/Hue.vue
  63. 108 0
      src/components/ColorPicker/Saturation.vue
  64. 443 0
      src/components/ColorPicker/index.vue
  65. 137 0
      src/components/Contextmenu/MenuContent.vue
  66. 80 0
      src/components/Contextmenu/index.vue
  67. 14 0
      src/components/Contextmenu/types.ts
  68. 34 0
      src/components/Divider.vue
  69. 126 0
      src/components/Drawer.vue
  70. 46 0
      src/components/FileInput.vue
  71. 71 0
      src/components/FullscreenSpin.vue
  72. 149 0
      src/components/GradientBar.vue
  73. 136 0
      src/components/Input.vue
  74. 57 0
      src/components/LaTeXEditor/FormulaContent.vue
  75. 20 0
      src/components/LaTeXEditor/SymbolContent.vue
  76. 5 0
      src/components/LaTeXEditor/hfmath.ts
  77. 265 0
      src/components/LaTeXEditor/index.vue
  78. 182 0
      src/components/Message.vue
  79. 154 0
      src/components/Modal.vue
  80. 220 0
      src/components/MoveablePanel.vue
  81. 201 0
      src/components/NumberInput.vue
  82. 350 0
      src/components/OutlineEditor.vue
  83. 111 0
      src/components/Popover.vue
  84. 38 0
      src/components/PopoverMenuItem.vue
  85. 26 0
      src/components/RadioButton.vue
  86. 35 0
      src/components/RadioGroup.vue
  87. 204 0
      src/components/Select.vue
  88. 120 0
      src/components/SelectCustom.vue
  89. 54 0
      src/components/SelectGroup.vue
  90. 280 0
      src/components/Slider.vue
  91. 84 0
      src/components/Switch.vue
  92. 108 0
      src/components/Tabs.vue
  93. 94 0
      src/components/TextArea.vue
  94. 38 0
      src/components/TextColorButton.vue
  95. 474 0
      src/components/WritingBoard.vue
  96. 234 0
      src/configs/animation.ts
  97. 70 0
      src/configs/chart.ts
  98. 22 0
      src/configs/element.ts
  99. 25 0
      src/configs/font.ts
  100. 149 0
      src/configs/hotkey.ts

+ 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

+ 661 - 0
LICENSE

@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published
+    by the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.

+ 238 - 0
README.md

@@ -0,0 +1,238 @@
+<p align="center">
+    <img src='/public/logo.png' />
+</p>
+
+<p align="center">
+    <a href="https://www.github.com/pipipi-pikachu/PPTist/stargazers" target="_black"><img src="https://img.shields.io/github/stars/pipipi-pikachu/PPTist?logo=github" alt="stars" /></a>
+    <a href="https://www.github.com/pipipi-pikachu/PPTist/network/members" target="_black"><img src="https://img.shields.io/github/forks/pipipi-pikachu/PPTist?logo=github" alt="forks" /></a>
+    <a href="https://www.github.com/pipipi-pikachu/PPTist/blob/master/LICENSE" target="_black"><img src="https://img.shields.io/github/license/pipipi-pikachu/PPTist?color=%232DCE89&logo=github" alt="license" /></a>
+    <a href="https://www.typescriptlang.org" target="_black"><img src="https://img.shields.io/badge/language-TypeScript-blue.svg" alt="language"></a>
+    <a href="https://github.com/pipipi-pikachu/PPTist/issues" target="_black"><img src="https://img.shields.io/github/issues-closed/pipipi-pikachu/PPTist.svg" alt="issue"></a>
+    <a href="https://gitee.com/pptist/PPTist" target="_black"><img src="https://gitee.com/pptist/PPTist/badge/star.svg?version=latest" alt="gitee"></a>
+</p>
+
+[简体中文](README_zh.md) | English
+
+
+# 🎨 PPTist
+> PowerPoint-ist(/'pauəpɔintist/), A web-based presentation (slideshow) application. This application replicates most of the commonly used features of Microsoft Office PowerPoint. It supports various essential element types such as text, images, shapes, lines, charts, tables, videos, audio, and formulas. You can edit and present slides directly in a web browser.
+
+<b>Try it online👉:[https://pipipi-pikachu.github.io/PPTist/](https://pipipi-pikachu.github.io/PPTist/)</b>
+
+# ✨ Highlights
+1. <b>Easy Development</b>: Built with Vue 3.x and TypeScript, it does not rely on UI component libraries and avoids third-party components as much as possible. This makes styling customization easier and functionality extension more convenient.
+2. <b>User Friendly</b>: It offers a context menu available everywhere, dozens of keyboard shortcuts, and countless editing detail optimizations, striving to replicate a desktop application-level experience.
+3. <b>Feature Rich</b>: Supports most of the commonly used elements and functionalities found in PowerPoint, supports generate PPT by AI, supports exporting in various formats, and offers basic editing and previewing on mobile devices.
+
+# 👀 Front-Row Reminder
+1. This project is a "Web Slideshow Application", not a "low-code platform", "H5 editor", "image editor", "whiteboard application", or similar tools.
+2. The target audience for this project is <b>developers with needs for [Web slideshow] development, basic web development experience is required</b>. The provided link is merely a demo address and does not offer any online services. You should not use this project directly as a tool, nor does it support out-of-the-box functionality. If you simply need a service or tool, you can opt for more excellent and mature products such as: [Slidev](https://sli.dev/)、[revealjs](https://revealjs.com/), etc.
+3. Here are some summarized [Frequently Asked Questions](/doc/Q&A.md). When raising Issues or submitting PRs for the first time, be sure to read this document in advance.
+
+
+# 🚀 Installation
+```
+npm install
+
+npm run dev
+```
+Browser access: http://127.0.0.1:5173/
+
+> Note: If you deploy this project on your own server and find that it fails to initialize, it's because the initialization data is stored in the author's private object storage and is not publicly accessible. You'll need to transfer the data to your own server, object storage service, database, or front-end local storage.
+
+
+# 📚 Features
+### Basic Features
+- History (undo, redo)
+- Shortcuts
+- Right-click menu
+- Export local files (PPTX, JSON, images, PDF)
+- Import and export pptist files
+- Print
+- AI PPT
+### Slide Page Editing
+- Add/delete pages
+- Copy/paste pages
+- Adjust page order
+- Create sections
+- Background settings (solid color, gradient, image)
+- Set canvas size
+- Gridlines
+- Rulers
+- Canvas zoom and move
+- Theme settings
+- Extract slides style
+- Speaker notes (rich text)
+- Slide templates
+- Transition animations
+- Element animations (entrance, exit, emphasis)
+- Selection panel (hide elements, layer sorting, element naming)
+- Labels for Page and Node Types (usable for template-related features)
+- Find/replace
+- Annotations
+### Slide Element Editing
+- Add/delete elements
+- Copy/paste elements
+- Drag and move elements
+- Rotate elements
+- Scale elements
+- Multiple element selection (marquee, point selection)
+- Group multiple elements
+- Batch edit multiple elements
+- Lock elements
+- Magnetic alignment of elements (move and scale)
+- Adjust element layer
+- Align elements to canvas
+- Align elements to other elements
+- Evenly distribute multiple elements
+- Drag to add text and images
+- Paste external images
+- Set element coordinates, size, and rotation
+- Element hyperlinks (link to webpage, link to other slide pages)
+#### Text
+- Rich text editing (color, highlight, font, font size, bold, italic, underline, strikethrough, subscript, inline code, quote, hyperlink, alignment, numbering, bullet points, paragraph indent, clear formatting)
+- Line height
+- Character spacing
+- Paragraph spacing
+- First line indent
+- Fill color
+- Border
+- Shadow
+- Transparency
+- Vertical text
+- AI Rewrite/Expand/Abbreviate
+#### Images
+- Crop (custom, shape, aspect ratio)
+- Rounding
+- Filters
+- Tint (mask)
+- Flip
+- Border
+- Shadow
+- Replace image
+- Reset image
+- Set as background
+#### Shapes
+- Draw any polygon
+- Draw any line (unclosed shape simulation)
+- Replace shape
+- Fill (solid color, gradient, image)
+- Border
+- Shadow
+- Transparency
+- Flip
+- Shape format painter
+- Edit text (supports rich text, similar to text element’s rich text editing)
+#### Lines
+- Straight lines, polylines, curves
+- Color
+- Width
+- Style (solid, dashed, dotted)
+- Endpoint style
+#### Charts (bar, column, line, area, scatter, pie, donut, radar)
+- Chart type conversion
+- Data editing
+- Background fill
+- Theme color
+- Coordinate system and axis text color
+- Grid color
+- Other chart settings
+- Border
+#### Tables
+- Add/delete rows and columns
+- Theme settings (theme color, header, total row, first column, last column)
+- Merge cells
+- Cell styles (fill color, text color, bold, italic, underline, strikethrough, alignment)
+- Border
+#### Video
+- Preview cover settings
+- Auto play
+#### Audio
+- Icon color
+- Auto play
+- Loop play
+#### Formulas
+- LaTeX editing
+- Color settings
+- Formula line thickness settings
+### Slide Show
+- Brush tools (pen/shape/arrow/highlighter annotation, eraser, blackboard mode)
+- Preview all slides
+- Bottom thumbnails navigation
+- Timer tool
+- Laser pointer
+- Auto play
+- Speaker view
+### Mobile
+- Basic editing
+  - Add/delete/copy/note/undo redo pages
+  - Insert text, images, rectangles, circles
+  - General element operations: move, scale, rotate, copy, delete, layer adjust, align
+  - Element styles: text (bold, italic, underline, strikethrough, font size, color, alignment), fill color
+- Basic preview
+- Play preview
+
+
+# 👀 FAQ
+Some common problems: [FAQ](/doc/Q&A.md)
+
+
+# 🎯 Supplement
+There is currently no complete development documentation, but the following documents may be of some help to you:
+- [Project Directory and Data Structure](/doc/DirectoryAndData.md)
+- [Fundamentals of Canvas and Elements](/doc/Canvas.md)
+- [How to Customize an Element](/doc/CustomElement.md)
+- [About AIPPT](/doc/AIPPT.md)
+
+Here are some auxiliary development tools/repositories:
+- Import PPTX file reference: [pptxtojson](https://github.com/pipipi-pikachu/pptxtojson)
+- Draw shape: [svgPathCreator](https://github.com/pipipi-pikachu/svgPathCreator)
+
+
+# 📄 License
+[AGPL-3.0 License](https://github.com/pipipi-pikachu/PPTist/blob/master/LICENSE) | Copyright © 2020-PRESENT [pipipi-pikachu](https://github.com/pipipi-pikachu)
+
+# 🧮 Commercial
+If you wish to use this project for commercial gain, I hope you will respect open source and strictly adhere to the AGPL-3.0 license, giving back to the open source community. Or contact the author for an independent commercial license.
+
+
+
+
+
+# 🧮 商业用途
+- 本项目禁止闭源商用,如果你希望将PPTist用于商业项目盈利,请尊重开源,**严格遵循 [AGPL-3.0 协议](https://www.gnu.org/licenses/agpl-3.0.html)**,回馈开源社区(这是作者倡导的);
+- 如果你因为任何原因,必须要闭源商用,无法执行 AGPL-3.0 协议,可以选择:
+    1. 使用早期的 Apache 2.0 协议版本 [(该版本最后更新时间为2022年5月,目前已停止维护,点击此处可下载代码)](https://github.com/pipipi-pikachu/PPTist/archive/f1a35bb8e045124e37dcafd6acbf40b4531b69aa.zip);
+    2. 成为项目的重要贡献者,包括:
+        - 你的代码被本项目作为依赖引用,包括:npm安装、script/style等文件引用、代码片段引用(引用处会注明);
+        - 你给本项目提交过重要的 PR 或 Issue(由作者主观判断,符合的 PR 或 Issue 会打上`important contribution`标签);
+        - 你长期参与到本项目的维护/推进工作中,如为本项目:提供了有效的周边工具、制作了大量模板等(由作者主观判断);
+        - 先违反协议后再成为贡献者不适用此项;
+    3. [邮件联系作者](mailto:pipipi_pikachu@163.com)付费获取独立的商业授权。独立授权价格:
+        - 一年1999元
+        - 三年2999元
+        - 永久5499元(不含税)
+        - 违反协议后被作者找到的,不适用于以上价格
+- 建议优先考虑执行AGPL-3.0协议,如需付费获取独立的商业授权,还请注意:
+    - 独立商业授权表示:单独授权您将代码用于商业行为,且不必执行 AGPL-3.0 协议;
+    - 仅授权(而非出售软件或服务),不存在其他“高级版本/付费版本/完整版本”、不提供任何在线服务/技术支持/技术咨询/定制开发、也不提供可直接交付的产品;
+    - 该软件无法开箱即用,至少也需自行接入后端数据读取/存储相关能力。因此,使用本项目需要有最基本的web开发经验(能理解什么是前端&后端、数据从哪来&怎么存、什么是接口、什么是跨域等);
+    - 授权后,仍禁止将源代码二次售卖、授权、开源或恶意传播;
+    - 授权后,如有需要,作者可提供当前AIPPT相关后台逻辑和当前模板数据以供参考(但都非常简单,无任何核心逻辑,更建议自己实现);
+    - 请务必提前做好基础调研,判断PPTist是否符合需求,包括:功能(是否能满足业务需求)和开发(是否接受当前技术栈/实现方案);
+    - 作者没法提供法律文件来证明“我的Github帐号属于我”,如介意这一点,请考虑其他途径;
+    - 不接受[黑名单/耻辱柱](/doc/Blacklist.md)对象通过付费获取独立的商业授权或贡献代码;
+    - 作者倡导异步沟通,**不加微信/QQ/手机号等**,有任何授权相关疑问请邮件联系,谢谢理解;
+    - 提需求/报bug/询问技术方案等非授权相关咨询,请在 [Issues](https://github.com/pipipi-pikachu/PPTist/issues) 中进行。
+
+---
+# 🔔 其他说明
+## 什么是 AGPL-3.0 协议
+协议的核心要求用通俗的语言解释如下:
+- **开源义务**:如果你用了 AGPL 的代码,无论你或你的下游怎么使用/修改,都必须把你最终的代码全部完整公开出来(不只是给出修改的部分,也不是说换个框架重写一遍就能和原始代码脱离关系了),并继续以 AGPL 协议开源,保持开源的传染性。
+- **网络服务也要开源**:即使你只是用 AGPL 的代码做了一个网站或网络服务,别人通过网络用你的服务时,你也需要遵守上一条**开源义务**。
+- **保留版权声明**:你不能删掉代码里原来的作者信息和许可证声明,得告诉大家这代码是从哪儿来的。
+- **不能加额外限制**:你不能在 AGPL 代码上加一些限制,比如不让别人再分发代码,或者要求别人付费才能使用代码。
+- **免责声明**:作者不保证代码没有 bug,也不对使用后果负责。
+
+详细协议内容见官方文档:[AGPL-3.0 协议](https://www.gnu.org/licenses/agpl-3.0.html)

+ 228 - 0
README_zh.md

@@ -0,0 +1,228 @@
+<p align="center">
+    <img src='/public/logo.png' />
+</p>
+
+<p align="center">
+    <a href="https://www.github.com/pipipi-pikachu/PPTist/stargazers" target="_black"><img src="https://img.shields.io/github/stars/pipipi-pikachu/PPTist?logo=github" alt="stars" /></a>
+    <a href="https://www.github.com/pipipi-pikachu/PPTist/network/members" target="_black"><img src="https://img.shields.io/github/forks/pipipi-pikachu/PPTist?logo=github" alt="forks" /></a>
+    <a href="https://www.github.com/pipipi-pikachu/PPTist/blob/master/LICENSE" target="_black"><img src="https://img.shields.io/github/license/pipipi-pikachu/PPTist?color=%232DCE89&logo=github" alt="license" /></a>
+    <a href="https://www.typescriptlang.org" target="_black"><img src="https://img.shields.io/badge/language-TypeScript-blue.svg" alt="language"></a>
+    <a href="https://github.com/pipipi-pikachu/PPTist/issues" target="_black"><img src="https://img.shields.io/github/issues-closed/pipipi-pikachu/PPTist.svg" alt="issue"></a>
+    <a href="https://gitee.com/pptist/PPTist" target="_black"><img src="https://gitee.com/pptist/PPTist/badge/star.svg?version=latest" alt="gitee"></a>
+</p>
+
+简体中文 | [English](README.md)
+
+
+# 🎨 PPTist
+> PowerPoint-ist(/'pauəpɔintist/),一个基于 Web 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能,支持 文字、图片、形状、线条、图表、表格、视频、音频、公式 几种最常用的元素类型,可以在 Web 浏览器中编辑/演示幻灯片。
+
+<b>在线体验地址👉:[https://pipipi-pikachu.github.io/PPTist/](https://pipipi-pikachu.github.io/PPTist/)</b>
+
+# ✨ 项目特色
+1. 易开发:基于 Vue3.x + TypeScript 构建,不依赖UI组件库,尽量避免第三方组件,样式定制更轻松、功能扩展更方便。
+2. 易使用:随处可用的右键菜单、几十种快捷键、无数次编辑细节打磨,力求还原桌面应用级体验。
+3. 功能丰富:支持 PPT 中的大部分常用元素和功能,支持AI生成PPT、支持多种格式导出、支持移动端基础编辑和预览...
+
+
+# 👀 前排提示
+1. 本项目是一个 “Web 幻灯片应用” ,而不是 “低代码平台”、“H5 编辑器”、“图片编辑器” 、“白板应用”等。
+2. 本项目的目标受众是<b>有Web幻灯片开发需求的开发者,需要有基础的web开发经验</b>,提供的链接只是一个演示地址,不提供任何在线服务。你不应该直接将本项目作为工具使用,也不支持开箱即用。如果你只是需要一个服务或工具,可以选择更优秀和成熟的产品,例如:[Slidev](https://sli.dev/)、[revealjs](https://revealjs.com/) 等。
+3. 这里总结了一些[常见问题](/doc/Q&A.md),第一次提 Issues 和 PR 时,务必提前阅读此文档。
+
+
+# 🚀 项目运行
+```
+npm install
+
+npm run dev
+```
+浏览器访问:http://127.0.0.1:5173/
+
+> 注意:如果你将本项目部署在自己的服务器上,发现无法初始化成功,那是因为初始化的数据是放在作者私人对象存储中的,不对外开放,你需要把数据转移到自己的服务器/对象存储服务/数据库/前端本地
+
+
+# 📚 功能列表
+### 基础功能
+- 历史记录(撤销、重做)
+- 快捷键
+- 右键菜单
+- 导出本地文件(PPTX、JSON、图片、PDF)
+- 导入导出特有 .pptist 文件
+- 打印
+- AI生成PPT
+### 幻灯片页面编辑
+- 页面添加、删除
+- 页面顺序调整
+- 页面复制粘贴
+- 幻灯片分节
+- 背景设置(纯色、渐变、图片)
+- 设置画布尺寸
+- 网格线
+- 标尺
+- 画布缩放、移动
+- 主题设置
+- 提取已有幻灯片风格
+- 演讲者备注(富文本)
+- 幻灯片模板
+- 翻页动画
+- 元素动画(入场、退场、强调)
+- 选择面板(隐藏元素、层级排序、元素命名)
+- 页面和节点类型标注(可用于模板相关功能)
+- 查找/替换
+- 批注
+### 幻灯片元素编辑
+- 元素添加、删除
+- 元素复制粘贴
+- 元素拖拽移动
+- 元素旋转
+- 元素缩放
+- 元素多选(框选、点选)
+- 多元素组合
+- 多元素批量编辑
+- 元素锁定
+- 元素吸附对齐(移动和缩放)
+- 元素层级调整
+- 元素对齐到画布
+- 元素对齐到其他元素
+- 多元素均匀分布
+- 拖拽添加图文
+- 粘贴外部图片
+- 元素坐标、尺寸和旋转角度设置
+- 元素超链接(链接到网页、链接到其他幻灯片页面)
+#### 文字
+- 富文本编辑(颜色、高亮、字体、字号、加粗、斜体、下划线、删除线、角标、行内代码、引用、超链接、对齐方式、序号、项目符号、段落缩进、清除格式)
+- 行高
+- 字间距
+- 段间距
+- 首行缩进
+- 填充色
+- 边框
+- 阴影
+- 透明度
+- 竖向文本
+- AI改写/扩写/缩写
+#### 图片
+- 裁剪(自定义、按形状、按纵横比)
+- 圆角
+- 滤镜
+- 着色(蒙版)
+- 翻转
+- 边框
+- 阴影
+- 替换图片
+- 重置图片
+- 设置为背景图
+#### 形状
+- 绘制任意多边形
+- 绘制任意线条(未封闭形状模拟)
+- 替换形状
+- 填充(纯色、渐变、图片)
+- 边框
+- 阴影
+- 透明度
+- 翻转
+- 形状格式刷
+- 编辑文字(支持富文本,与文字元素的富文本编辑功能近似)
+#### 线条
+- 直线、基础折线/曲线
+- 颜色
+- 宽度
+- 样式(实线、虚线、点线)
+- 端点样式
+#### 图表(柱状图、条形图、折线图、面积图、散点图、饼图、环形图、雷达图)
+- 图表类型转换
+- 数据编辑
+- 背景填充
+- 主题色
+- 坐标轴/坐标文字颜色
+- 网格颜色
+- 堆积模式、平滑曲线等
+#### 表格
+- 行、列添加删除
+- 主题设置(主题色、表头、汇总行、第一列、最后一列)
+- 合并单元格
+- 单元格样式(填充色、文字颜色、加粗、斜体、下划线、删除线、对齐方式)
+- 边框
+#### 视频
+- 预览封面设置
+- 自动播放
+#### 音频
+- 图标颜色
+- 自动播放
+- 循环播放
+#### 公式
+- LaTeX编辑
+- 颜色设置
+- 公式线条粗细设置
+### 幻灯片放映
+- 画笔工具(画笔/形状/箭头/荧光笔标注、橡皮擦除、黑板模式)
+- 全部幻灯片预览
+- 触底显示缩略图导航
+- 计时器工具
+- 激光笔
+- 自动放映
+- 演讲者视图
+### 移动端
+- 基础编辑
+    - 页面添加、删除、复制、备注、撤销重做
+    - 插入文字、图片、矩形、圆形
+    - 元素通用操作:移动、缩放、旋转、复制、删除、层级调整、对齐
+    - 元素样式:文字(加粗、斜体、下划线、删除线、字号、颜色、对齐方向)、填充色
+- 基础预览
+- 播放预览
+
+
+# 🎯 开发
+目前没有完整的开发文档,但下面这些文档可能会对你有一些帮助:
+- [项目目录与数据结构](/doc/DirectoryAndData.md)
+- [画布与元素的基本原理](/doc/Canvas.md)
+- [如何自定义一个元素](/doc/CustomElement.md)
+- [关于AIPPT](/doc/AIPPT.md)
+
+下面是一些辅助开发的工具/仓库:
+- 导入PPTX文件参考:[pptxtojson](https://github.com/pipipi-pikachu/pptxtojson)
+- 绘制形状:[svgPathCreator](https://github.com/pipipi-pikachu/svgPathCreator)
+
+
+# 📄 版权声明/开源协议
+[AGPL-3.0 License](/LICENSE) | Copyright © 2020-PRESENT [pipipi-pikachu](https://github.com/pipipi-pikachu)
+
+
+# 🧮 商业用途
+- 本项目禁止闭源商用,如果你希望将PPTist用于商业项目盈利,请尊重开源,**严格遵循 [AGPL-3.0 协议](https://www.gnu.org/licenses/agpl-3.0.html)**,回馈开源社区(这是作者倡导的);
+- 如果你因为任何原因,必须要闭源商用,无法执行 AGPL-3.0 协议,可以选择:
+    1. 使用早期的 Apache 2.0 协议版本 [(该版本最后更新时间为2022年5月,目前已停止维护,点击此处可下载代码)](https://github.com/pipipi-pikachu/PPTist/archive/f1a35bb8e045124e37dcafd6acbf40b4531b69aa.zip);
+    2. 成为项目的重要贡献者,包括:
+        - 你的代码被本项目作为依赖引用,包括:npm安装、script/style等文件引用、代码片段引用(引用处会注明);
+        - 你给本项目提交过重要的 PR 或 Issue(由作者主观判断,符合的 PR 或 Issue 会打上`important contribution`标签);
+        - 你长期参与到本项目的维护/推进工作中,如为本项目:提供了有效的周边工具、制作了大量模板等(由作者主观判断);
+        - 先违反协议后再成为贡献者不适用此项;
+    3. [邮件联系作者](mailto:pipipi_pikachu@163.com)付费获取独立的商业授权。独立授权价格:
+        - 一年1999元
+        - 三年2999元
+        - 永久5499元(不含税)
+        - 违反协议后被作者找到的,不适用于以上价格
+- 建议优先考虑执行AGPL-3.0协议,如需付费获取独立的商业授权,还请注意:
+    - 独立商业授权表示:单独授权您将代码用于商业行为,且不必执行 AGPL-3.0 协议;
+    - 仅授权(而非出售软件或服务),不存在其他“高级版本/付费版本/完整版本”、不提供任何在线服务/技术支持/技术咨询/定制开发、也不提供可直接交付的产品;
+    - 该软件无法开箱即用,至少也需自行接入后端数据读取/存储相关能力。因此,使用本项目需要有最基本的web开发经验(能理解什么是前端&后端、数据从哪来&怎么存、什么是接口、什么是跨域等);
+    - 授权后,仍禁止将源代码二次售卖、授权、开源或恶意传播;
+    - 授权后,如有需要,作者可提供当前AIPPT相关后台逻辑和当前模板数据以供参考(但都非常简单,无任何核心逻辑,更建议自己实现);
+    - 请务必提前做好基础调研,判断PPTist是否符合需求,包括:功能(是否能满足业务需求)和开发(是否接受当前技术栈/实现方案);
+    - 作者没法提供法律文件来证明“我的Github帐号属于我”,如介意这一点,请考虑其他途径;
+    - 不接受[黑名单/耻辱柱](/doc/Blacklist.md)对象通过付费获取独立的商业授权或贡献代码;
+    - 作者倡导异步沟通,**不加微信/QQ/手机号等**,有任何授权相关疑问请邮件联系,谢谢理解;
+    - 提需求/报bug/询问技术方案等非授权相关咨询,请在 [Issues](https://github.com/pipipi-pikachu/PPTist/issues) 中进行。
+
+---
+# 🔔 其他说明
+## 什么是 AGPL-3.0 协议
+协议的核心要求用通俗的语言解释如下:
+- **开源义务**:如果你用了 AGPL 的代码,无论你或你的下游怎么使用/修改,都必须把你最终的代码全部完整公开出来(不只是给出修改的部分,也不是说换个框架重写一遍就能和原始代码脱离关系了),并继续以 AGPL 协议开源,保持开源的传染性。
+- **网络服务也要开源**:即使你只是用 AGPL 的代码做了一个网站或网络服务,别人通过网络用你的服务时,你也需要遵守上一条**开源义务**。
+- **保留版权声明**:你不能删掉代码里原来的作者信息和许可证声明,得告诉大家这代码是从哪儿来的。
+- **不能加额外限制**:你不能在 AGPL 代码上加一些限制,比如不让别人再分发代码,或者要求别人付费才能使用代码。
+- **免责声明**:作者不保证代码没有 bug,也不对使用后果负责。
+
+详细协议内容见官方文档:[AGPL-3.0 协议](https://www.gnu.org/licenses/agpl-3.0.html)

+ 16 - 0
commitlint.config.cjs

@@ -0,0 +1,16 @@
+/**
+ * build 编译相关的修改,例如发布版本、对项目构建或者依赖的改动
+ * chore 其他修改, 比如改变构建流程、或者增加依赖库、工具等
+ * docs 文档修改
+ * feat 新特性、新功能
+ * fix 修改bug
+ * perf 优化相关,比如提升性能、体验
+ * refactor 代码重构
+ * revert 回滚到上一个版本
+ * style 代码格式修改
+ * test 测试用例修改
+ */
+/* eslint-env node */
+module.exports = {
+  extends: ['@commitlint/config-conventional'],
+}

+ 61 - 0
doc/AIPPT.md

@@ -0,0 +1,61 @@
+## AIPPT的基本原理
+1. 定义PPT结构(一套PPT中都有什么类型的页面,每种页面都有些什么内容);
+2. 基于以上结构,定义数据格式,该数据将用于AI生成结构化的PPT数据,具体结构见:
+    - 示例数据:`public/mocks/AIPPT.json`
+    - 结构定义:`src/types/AIPPT.ts`
+3. 制作模板,模板中标记好结构类型;
+4. AI生成符合第1步定义的PPT结构的数据;
+5. 利用AI或其他方案,生成相关的配图(常见途径有:AI文生图、图库搜索匹配);
+6. 将AI生成的数据、配图与模板进行匹配结合,生成最终的PPT。
+
+> 注:虽然当前线上版本不提供配图演示效果,但是AIPPT的方法是支持此功能的,你只需要自己提供图片源,按照要求的格式将待选图片集合传入AIPPT方法即可。
+
+## AIPPT模板制作流程
+1. 打开PPTist;
+2. 制作模板页面;
+3. 打开左上角菜单[幻灯片类型标注]功能;
+4. 为制作好的页面标注页面类型和节点类型;
+5. 使用导出功能导出为JSON文件。
+
+> 注意:实际上并不存在专门提供给AIPPT的模板。所谓的AIPPT模板只是把在PPTist中制作的普通页面标注上类型标记而已。这些数据不仅仅用于AI生成PPT,也可以作为普通的页面模板使用。
+
+## 模板标记类型:页面标记和节点标记
+#### 封面页
+* 标题
+* 正文
+* 图片(背景图、页面插图)
+#### 目录页
+* 目录标题(标记类型为:列表项目)
+* 图片(背景图、页面插图)
+#### 过渡页(章节过渡)
+* 标题
+* 正文
+* 节编号
+* 图片(背景图、页面插图)
+#### 内容页
+* 标题
+* 2~4个内容项,包括:
+  * 内容项标题(标记类型为:列表项标题)
+  * 内容项正文(标记类型为:列表项目)
+  * 内容项编号(标记类型为:项目编号)
+* 图片(背景图、页面插图、项目插图)
+#### 结束页(致谢页)
+* 图片(背景图、页面插图)
+
+> 节点标记分为两种 - 文本标记和图片标记:
+> - 文本标记可作用于文本节点和带文字的形状节点;
+> - 图片标记只作用于图片节点;
+> - 你可以自行添加更多类型的标记(如图表)。
+
+## AIPPT模板制作原则
+一个用于AIPPT的模板至少包括以下页面(共12页):
+* 1个封面页
+* 6个目录页(2~6个目录项各1个,10个目录项的1个)
+* 1个过渡页
+* 3个内容页(2~4个内容项各1个)
+* 1个结束页
+
+> 注意:
+> 1. 以上页数仅满足当前替换逻辑的最基本要求,如果希望AI生成的PPT具有一定的随机性,需要适当增加每种页面的数量(举个例子,假设模板中存在3个封面页,生成时会从3个中随机选择1个使用);
+> 2. 当前替换逻辑下,目录页可支持1~20个目录项,内容页可支持1~12个内容项,但不需要每种数量的模板都做一遍,因为程序会自动通过模板的拼接/裁减方式来实现特殊的项目数;
+> 3. 你可以自行调整替换逻辑,以支持更多情况。

+ 5 - 0
doc/Blacklist.md

@@ -0,0 +1,5 @@
+# 🤮 黑名单/耻辱柱
+
+👎蚂蚁PPT([宁波灵达网络科技有限公司](https://aiqicha.baidu.com/company_detail_45305048629737)) - https://www.antppt.com/
+
+> 未按AGPL-3协议开源相关代码,并删除了原始版权声明。违反协议数月后被作者找上,自知侵权行为却毫无歉意,坚持拒绝遵守AGPL-3协议,期间反倒试图让作者帮助其解决程序问题,甚至还要求作者提供手持身份证照片自证

+ 67 - 0
doc/Canvas.md

@@ -0,0 +1,67 @@
+## 画布与元素
+
+#### 编辑器的基本结构
+```
+└──编辑器
+    ├── 顶部菜单栏
+    ├── 左侧导航栏
+    ├── 右侧导航栏
+    ├── 中上部插入/工具栏
+    ├── 底部演讲者备注
+    └── 画布
+         ├── 可视区域
+         │    ├── 可编辑元素
+         │    └── 鼠标选框
+         │
+         └── 画布工具
+              ├── 参考线
+              ├── 标尺
+              ├── 元素操作节点层(如拖拽缩放点)
+              ├── 吸附对齐线
+              └── 可视区域背景
+```
+
+#### 画布的基本原理
+我们把关注点放在相对复杂的【画布】部分。画布中的每一个元素都由一组数据来描述,例如:
+```typescript
+interface PPTBaseElement {
+  id: string;
+  left: number;
+  top: number;
+  width: number;
+  height: number;
+}
+```
+顾名思义,`left` 表示元素距离画布左上角的位置,`width` 表示元素的宽度,以此类推。
+重点需要知道的是:可视区域默认以 宽1000像素 、高562.5像素为基础比例。即无论画布和可视区域实际大小是多少,一个 `{ width: 1000px, height: 562.5px, left: 0, top: 0 }` 的元素一定会正好铺满整个可视区域。
+具体实现的方法很简单:假设可视区域的实际宽度为 1200px ,计算出此时的缩放比为 1200 / 1000 = 1.2 ,然后将可视区域内的元素全部缩放到 1.2 倍即可。
+同理【缩略图】 和 【放映页面】 其实上就是一个实际大小更小或更大的可视区域。
+> 注:1000×562.5的宽高是可以通过修改`src/store/slides.ts`中的`viewportSize`来调整的。
+
+#### 画布内的元素
+除了上述中的位置和尺寸信息,还可以携带更多的数据,以一个文本元素为例:
+```typescript
+interface PPTTextElement {
+  type: 'text';
+  id: string;
+  left: number;
+  top: number;
+  lock?: boolean;
+  groupId?: string;
+  width: number;
+  height: number;
+  link?: string;
+  content: string;
+  rotate: number;
+  defaultFontName: string;
+  defaultColor: string;
+  outline?: PPTElementOutline;
+  fill?: string;
+  lineHeight?: number;
+  wordSpace?: number;
+  opacity?: number;
+  shadow?: PPTElementShadow;
+}
+```
+你可以定义一个 `rotate` 来表示文本框旋转的角度、定义一个 `opacity` 来表示文本框的透明度 等。在实现时只需要按照你所定义的数据来渲染元素组件即可,而编辑元素的本质就是在修改这些数据。
+以上就是一个画布最基本的组成了。

+ 414 - 0
doc/CustomElement.md

@@ -0,0 +1,414 @@
+## 如何自定义一个元素
+
+我们以【网页元素】为例,来梳理下自定义一个元素的过程。
+> 完整代码在 https://github.com/pipipi-pikachu/PPTist/tree/document-demo
+
+> 注意:由于版本更新,该文档和仓库中的代码并不是直接复制粘贴就可以使用,这里仅提供思路。
+
+### 编写新元素的结构与配置
+首先需要定义这个元素的结构,并添加该元素类型
+```typescript 
+// types/slides.ts
+
+export const enum ElementTypes {
+  TEXT = 'text',
+  IMAGE = 'image',
+  SHAPE = 'shape',
+  LINE = 'line',
+  CHART = 'chart',
+  TABLE = 'table',
+  LATEX = 'latex',
+  VIDEO = 'video',
+  AUDIO = 'audio',
+  FRAME = 'frame', // add
+}
+
+// add
+export interface PPTFrameElement extends PPTBaseElement {
+  type: 'frame'
+  id: string;
+  left: number;
+  top: number;
+  width: number;
+  height: number;
+  url: string; // 网页链接地址
+}
+
+// 修改 PPTElement Type
+export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PPTLineElement | PPTChartElement | PPTTableElement | PPTLatexElement | PPTVideoElement | PPTAudioElement | PPTFrameElement
+```
+
+在配置文件中添加新元素的中文名,以及最小尺寸:
+```typescript
+// configs/element
+
+export const ELEMENT_TYPE_ZH = {
+  text: '文本',
+  image: '图片',
+  shape: '形状',
+  line: '线条',
+  chart: '图表',
+  table: '表格',
+  video: '视频',
+  audio: '音频',
+  frame: '网页', // add
+}
+
+export const MIN_SIZE = {
+  text: 20,
+  image: 20,
+  shape: 15,
+  chart: 200,
+  table: 20,
+  video: 250,
+  audio: 20,
+  frame: 200, // add
+}
+```
+
+### 编写新元素组件
+然后开始编写该元素的组件:
+```html
+<!-- views/components/element/FrameElement/index.vue -->
+
+<template>
+  <div class="editable-element-frame"
+    :style="{
+      top: elementInfo.top + 'px',
+      left: elementInfo.left + 'px',
+      width: elementInfo.width + 'px',
+      height: elementInfo.height + 'px',
+    }"
+  >
+    <div
+      class="rotate-wrapper"
+      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
+    >
+      <div 
+        class="element-content" 
+        v-contextmenu="contextmenus"
+        @mousedown="$event => handleSelectElement($event)"
+        @touchstart="$event => handleSelectElement($event)"
+      >
+        <iframe 
+          :src="elementInfo.url"
+          :width="elementInfo.width"
+          :height="elementInfo.height"
+          :frameborder="0" 
+          :allowfullscreen="true"
+        ></iframe>
+
+        <div class="drag-handler top"></div>
+        <div class="drag-handler bottom"></div>
+        <div class="drag-handler left"></div>
+        <div class="drag-handler right"></div>
+
+        <div class="mask" 
+          v-if="handleElementId !== elementInfo.id"
+          @mousedown="$event => handleSelectElement($event, false)"
+          @touchstart="$event => handleSelectElement($event, false)"
+        ></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { PropType } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useMainStore } from '@/store'
+import { PPTFrameElement } from '@/types/slides'
+import { ContextmenuItem } from '@/components/Contextmenu/types'
+
+const props = defineProps({
+  elementInfo: {
+    type: Object as PropType<PPTFrameElement>,
+    required: true,
+  },
+  selectElement: {
+    type: Function as PropType<(e: MouseEvent | TouchEvent, element: PPTFrameElement, canMove?: boolean) => void>,
+    required: true,
+  },
+  contextmenus: {
+    type: Function as PropType<() => ContextmenuItem[] | null>,
+  },
+})
+
+const { handleElementId } = storeToRefs(useMainStore())
+
+const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
+  e.stopPropagation()
+  props.selectElement(e, props.elementInfo, canMove)
+}
+</script>
+
+<style lang="scss" scoped>
+.editable-element-frame {
+  position: absolute;
+}
+.element-content {
+  width: 100%;
+  height: 100%;
+  cursor: move;
+}
+.drag-handler {
+  position: absolute;
+
+  &.top {
+    height: 20px;
+    left: 0;
+    right: 0;
+    top: 0;
+  }
+  &.bottom {
+    height: 20px;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+  &.left {
+    width: 20px;
+    top: 0;
+    bottom: 0;
+    left: 0;
+  }
+  &.right {
+    width: 20px;
+    top: 0;
+    bottom: 0;
+    right: 0;
+  }
+}
+.mask {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+</style>
+```
+
+此外我们需要另一个不带编辑功能的基础版组件,用于缩略图/放映模式下显示:
+```html
+<!-- views/components/element/FrameElement/BaseFrameElement.vue -->
+
+<template>
+  <div class="base-element-frame"
+    :style="{
+      top: elementInfo.top + 'px',
+      left: elementInfo.left + 'px',
+      width: elementInfo.width + 'px',
+      height: elementInfo.height + 'px',
+    }"
+  >
+    <div
+      class="rotate-wrapper"
+      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
+    >
+      <div class="element-content">
+        <iframe 
+          :src="elementInfo.url"
+          :width="elementInfo.width"
+          :height="elementInfo.height"
+          :frameborder="0" 
+          :allowfullscreen="true"
+        ></iframe>
+
+        <div class="mask"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { PropType } from 'vue'
+import { PPTFrameElement } from '@/types/slides'
+
+const props = defineProps({
+  elementInfo: {
+    type: Object as PropType<PPTFrameElement>,
+    required: true,
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.base-element-frame {
+  position: absolute;
+}
+.element-content {
+  width: 100%;
+  height: 100%;
+}
+.mask {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+</style>
+```
+
+在这里你可能会发现,这两个组件非常相似,确实如此,对于比较简单的元素组件来说,可编辑版和不可编辑版是高度一致的,不可编辑版可能仅仅是少了一些方法而已。但是对于比较复杂的元素组件,两者的差异就会比较大了(具体可以比较文本元素和图片元素的两版),因此,你可以自行判断是否将二者合并抽象为一个组件,这里不过多展开。
+
+编写完元素组件,我们需要把它用在需要的地方,具体可能包括:
+
+- 缩略图元素组件 `views/components/ThumbnailSlide/ThumbnailElement.vue`
+- 放映元素组件 `views/Screen/ScreenElement.vue`
+- 可编辑元素组件 `views/Editor/Canvas/EditableElement.vue`
+- 移动端可编辑元素组件 `views/Mobile/MobileEditor/MobileEditableElement.vue`
+
+一般来说,前两者使用不可编辑版,后两者使用可编辑版。
+这里仅以画布中的可编辑元素组件为例:
+```html
+<!-- views/Editor/Canvas/EditableElement.vue -->
+
+<script lang="ts" setup>
+ import FrameElement from '@/views/components/element/FrameElement/index.vue'
+
+ const currentElementComponent = computed(() => {
+  const elementTypeMap = {
+    [ElementTypes.IMAGE]: ImageElement,
+    [ElementTypes.TEXT]: TextElement,
+    [ElementTypes.SHAPE]: ShapeElement,
+    [ElementTypes.LINE]: LineElement,
+    [ElementTypes.CHART]: ChartElement,
+    [ElementTypes.TABLE]: TableElement,
+    [ElementTypes.LATEX]: LatexElement,
+    [ElementTypes.VIDEO]: VideoElement,
+    [ElementTypes.AUDIO]: AudioElement,
+    [ElementTypes.FRAME]: FrameElement, // add
+  }
+  return elementTypeMap[props.elementInfo.type] || null
+})
+</script>
+```
+
+在画布的可编辑元素中,还需要为元素添加操作节点 `Operate`(一般包括八个缩放点、四条边线、一个旋转点),对于特殊的元素(如线条的操作节点明显与其他不同)你可以自己编写该组件,但是一般情况下可以直接使用已经编写好的通用操作节点:
+```html
+<!-- src\views\Editor\Canvas\Operate\index.vue -->
+
+<script lang="ts" setup>
+const currentOperateComponent = computed(() => {
+  const elementTypeMap = {
+    [ElementTypes.IMAGE]: ImageElementOperate,
+    [ElementTypes.TEXT]: TextElementOperate,
+    [ElementTypes.SHAPE]: ShapeElementOperate,
+    [ElementTypes.LINE]: LineElementOperate,
+    [ElementTypes.TABLE]: TableElementOperate,
+    [ElementTypes.CHART]: CommonElementOperate,
+    [ElementTypes.LATEX]: CommonElementOperate,
+    [ElementTypes.VIDEO]: CommonElementOperate,
+    [ElementTypes.AUDIO]: CommonElementOperate,
+    [ElementTypes.FRAME]: CommonElementOperate, // add
+  }
+  return elementTypeMap[props.elementInfo.type] || null
+})
+</script>
+```
+
+### 编写右侧元素编辑面板
+接下来需要为元素添加一个样式面板。当选中元素时,右侧工具栏会自动聚焦到该面板,你需要在这里添加一些你认为需要的设置项来操作元素本身,只需要记住一点:修改元素实际是修改元素的数据,也就是最开始定义的结构中的各个字段。
+另外,修改元素后不要忘了将操作添加到历史记录。
+```html
+<!-- src\views\Editor\Toolbar\ElementStylePanel\FrameStylePanel.vue -->
+
+<template>
+  <div class="frame-style-panel">
+    <div class="row">
+      <div>网页链接:</div>
+      <Input v-model:value="url" placeholder="请输入网页链接" />
+      <Button @click="updateURL()">确定</Button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useMainStore, useSlidesStore } from '@/store'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
+
+const slidesStore = useSlidesStore()
+const { handleElementId } = storeToRefs(useMainStore())
+
+const { addHistorySnapshot } = useHistorySnapshot()
+
+const url = ref('')
+
+const updateURL = () => {
+  if (!handleElementId.value) return
+  slidesStore.updateElement({ id: handleElementId.value, props: { url: url.value } })
+  addHistorySnapshot()
+}
+</script>
+```
+```html
+<script lang="ts" setup>
+import FrameStylePanel from './FrameStylePanel.vue'
+  
+const panelMap = {
+  [ElementTypes.TEXT]: TextStylePanel,
+  [ElementTypes.IMAGE]: ImageStylePanel,
+  [ElementTypes.SHAPE]: ShapeStylePanel,
+  [ElementTypes.LINE]: LineStylePanel,
+  [ElementTypes.CHART]: ChartStylePanel,
+  [ElementTypes.TABLE]: TableStylePanel,
+  [ElementTypes.LATEX]: LatexStylePanel,
+  [ElementTypes.VIDEO]: VideoStylePanel,
+  [ElementTypes.AUDIO]: AudioStylePanel,
+  [ElementTypes.FRAME]: FrameStylePanel, // add
+}
+</script>
+```
+
+### 创建元素
+这是自定义一个新元素的最后一步。首先编写一个创建元素的方法:
+```typescript
+// src\hooks\useCreateElement.ts
+
+const createFrameElement = (url: string) => {
+  createElement({
+    type: 'frame',
+    id: nanoid(10),
+    width: 800,
+    height: 480,
+    rotate: 0,
+    left: (VIEWPORT_SIZE - 800) / 2,
+    top: (VIEWPORT_SIZE * viewportRatio.value - 480) / 2,
+    url,
+  })
+}
+```
+然后在插入工具栏中使用:
+```html
+<!-- src\views\Editor\CanvasTool\index.vue -->
+
+<template>
+  <div class="canvas-tool">
+    <div class="add-element-handler">
+      <!-- add -->
+      <span class="handler-item" @click="createFrameElement('https://v3.cn.vuejs.org/')">插入网页</span>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+const {
+  createImageElement,
+  createChartElement,
+  createTableElement,
+  createLatexElement,
+  createVideoElement,
+  createAudioElement,
+  createFrameElement, // add
+} = useCreateElement()
+</script>
+```
+点击【插入网页】按钮,你就会看到一个网页元素被添加到画布中了。
+
+### 总结
+至此就是自定义一个元素的基本流程了。整个过程比较繁琐,但并不复杂,重点在于元素结构的定义与元素组件的编写,这决定了新元素将具备怎样的能力与外表。而其他的部分仅依葫芦画瓢即可。
+除此之外,还有一些非必须的调整:比如你希望导出能够支持新元素,则需要在导出相关的方法中进行扩展;比如你希望主题功能能够应用在新元素上,则需要在主题相关的方法中进行扩展,以此类推。

+ 43 - 0
doc/DirectoryAndData.md

@@ -0,0 +1,43 @@
+## 项目目录与数据结构
+
+### 项目目录结构
+```
+├── assets                        // 静态资源
+│   ├── fonts                     // 在线字体文件
+│   └── styles                    // 样式
+│       ├── antd.scss             // antd默认样式覆盖
+│       ├── font.scss             // 在线字体定义
+│       ├── global.scss           // 通用全局样式
+│       ├── mixin.scss            // scss全局混入
+│       ├── variable.scss         // scss全局变量
+│       └── prosemirror.scss      // ProseMirror 富文本默认样式
+├── components                    // 与业务逻辑无关的通用组件
+├── configs                       // 配置文件,如:画布尺寸、字体、动画配置、快捷键配置、预置形状、预置线条等数据。
+├── hooks                         // 供多个组件(模块)使用的 hooks 方法
+├── mocks                         // mocks 数据
+├── plugins                       // 自定义的 Vue 插件
+├── services                      // API方法
+├── types                         // 类型定义文件
+├── store                         // Pinia store,参考:https://pinia.vuejs.org/
+├── utils                         // 通用的工具方法
+└── views                         // 业务组件目录,分为 `编辑器` 和 `播放器` 两个部分。
+    ├── components                // 公用的业务组件
+    ├── Editor                    // 编辑器模块
+    ├── Screen                    // 播放器模块
+    └── Mobile                    // 移动端模块
+```
+
+
+### 数据
+幻灯片的数据主要存放在 `src/store/slides.ts` 中。
+> 换句话说,在实际的生产环境中,一般需要将该文件内state中的数据(部分)保存到数据库。
+
+包括:
+- `title` 幻灯片标题/文件名
+- `slides` 幻灯片页面数据,包括每一页的ID、元素内容、备注、背景、动画、切页方式等信息
+- `theme` 幻灯片主题数据,包括背景色、主题色、字体颜色、字体等信息
+- `viewportSize` 幻灯片可视区域宽度基数(默认1000,即1000×562.5的画布)
+- `viewportRatio` 幻灯片可视区域比例(宽:高),默认16:9
+- `templates` 幻灯片模板
+
+具体类型的定义可见:[完整数据类型定义](https://github.com/pipipi-pikachu/PPTist/blob/master/src/types/slides.ts)

+ 157 - 0
doc/Q&A.md

@@ -0,0 +1,157 @@
+## 常见问题
+
+#### Q. 为什么xxx快捷键没有作用?
+
+A. 部分快捷键需要聚焦到指定区域才会生效,例如焦点在左边缩略图列表才能使用操作页面的快捷键,焦点在画布区域才能使用操作元素的快捷键。
+
+#### Q. 为什么粘贴没有作用?
+
+A. 请注意允许浏览器访问系统剪贴板。
+
+#### Q. 为什么浏览器刷新或重新打开后,之前做的PPT没有了?
+
+A. 仓库提供的链接仅供演示,且该项目是纯前端部署的,没有后台,不会保存数据。
+
+#### Q. 如何调整幻灯片页面的顺序?
+
+A. 按住左侧缩略图可进行拖拽调整顺序。
+
+#### Q. 为什么插入图片后会出现操作卡顿的情况?
+
+A. 由于本演示项目不依赖后端,插入本地图片实际引用的是Base64,导致数据体积非常大,在真正的生产环境中应该上传图片后引用图片地址,就不会出现这样的情况了。
+
+#### Q. 为什么应用预置主题后没有效果?
+
+A. 设置预置主题的作用是使新添加的元素和页面应用主题样式,不会对已有的元素和页面生效,您可以使用“应用主题到全部”功能,将当前主题应用到全部页面中。
+
+#### Q. 设置在线字体不生效?
+
+A. 设置在线字体时会下载对应的字体文件,该文件较大,需要等待下载完成后才会应用新的字体。
+
+#### Q. 关于导入导出PPTX文件
+
+A. 作为一个在线幻灯片应用,导出、导入 PPTX 文件是非常重要的功能,但是经过调研发现,该功能实现起来的复杂度远超过了预期。由于个人能力和时间有限,这部分功能只能借助第三方的轮子来完成。
+
+- 导出:目前导出功能主要基于 [PptxGenJS](https://github.com/gitbrent/PptxGenJS/) 完成,能够实现大多数基本元素的导出,但还有非常多的缺陷需要一点点完善。同时需要知晓的是:1、该功能依赖 PptxGenJS,对于该库本身无法实现的部分(如动画),本项目也无能为力;2、导出功能的目标只是【导出样式尽可能一致的元素】,而不是一比一将网页还原到PPT,一些样式差异是必然存在的。
+
+- 导入:导入功能目前暂时没有合适的解决方案,还在调研和观望中。如果有感兴趣或做过相关内容的朋友,欢迎来 issues 中讨论。
+
+> PS. 我做了一个 [pptx转json](https://github.com/pipipi-pikachu/pptx2json) 的实验,如果你急需实现导入PPTX文件功能,可以此为参考自行实现。
+
+同时补充一点,本项目不是 office PPT 的专属在线编辑器,本质上与 office PPT 没有任何关系。【导入/导出 ppt 文件】只是项目的一个[功能]而非[目的]。
+
+#### Q. 视频元素支持哪些格式?
+
+A. 本项目只提供最基础的视频能力,正常状态下可以播放video标签本身支持的格式。
+
+此外,可以额外引入 [hls.js](https://github.com/video-dev/hls.js) 或 [flv.js](https://github.com/Bilibili/flv.js) 来支持对应的格式(.m3u8 .flv),你只需要在项目中引入对应的文件(如cdn)即可,无需其他配置。
+
+#### Q. 关于导入JSON文件
+
+A. 首先,出于安全等原因,个人并不建议将这种功能在前端直接暴露给用户,或者说用户根本就不应该接触到JSON这种格式(甚至导出JSON功能的初衷也只是为了方便开发)。如果真的有相关的需求,请自行在服务端实现,核心在于做好进行数据的校验,前端实现也是一样。
+
+#### Q. 打印 / 导出 PDF 样式与实际有出入
+
+A. 请注意在浏览器弹出的打印窗口调整相关的设置。建议:设置边距为【默认】、取消勾选【页眉和页脚】、勾选【背景图形】。另外,建议在正式环境中采用后端生成PDF的方案效果更佳(如puppeteer)。
+
+#### Q. 为什么移动端不支持 xxx 功能?
+
+A. 首先需要明确的一点,就是移动端无论怎么做,体验上都是必然大不如 PC 端的。因此个人将移动端定位为:简单进行一些临时处理的应急使用。真正的设计/制作幻灯片应在电脑上使用完整的功能。如实在有移动端的特殊需求,可尝试在移动端使用电脑模式打开(当然,体验会更槽糕),或者开发者自己进行二次开发。
+
+#### Q. 关于兼容性?
+
+A. 本项目优先兼容Chrome、Firefox。在Safari下可能存在部分兼容性问题。不兼容IE。
+
+#### Q. 为什么不是NPM包?
+
+A. 大家都知道,对于一般的插件/库而言,一个封装好的npm包能够更方便的接入现有的项目中,但PPTist是特殊的,这是一个完整的程序,而不是作为程序的一部分存在。如果你需要使用PPTist,那么我认为你必然需要在此基础上做很多定制化的开发,包括但不限于:与后台的通信、各种模板和预置素材、新的元素类型、使用其他方案实现现有的某些元素、自己的主题、更换快捷键,等等……而不是仅仅安装一个和现有demo一样的东西就行了(这样虽然方便,但在实际的产品开发中没有任何意义)。正如前面所列举的,需要可配置的东西太多了,如果作为一个插件的存在,很难兼顾得了,或者说这样做的开发量是巨大的,目前个人还承担不起。
+
+因此,使用PPTist开发项目正确的做法是:拉取完整的代码、尝试理解它、基于它改造你自己的东西。社区中也不乏类似的项目,例如 [drawio](https://github.com/jgraph/drawio)
+
+#### Q. 关于 AI PPT
+
+A. 首先需要说明,AIPPT不是PPTist的重点,现在或以后都不是,它只是PPTist众多功能中非常小的一部分而已,并且是比较简单一部分,你可以理解为这只是一个跟风的小功能点,我不想蹭AI的热度,但无奈太多人将AI看得太重要太复杂了,于是我做了这个DEMO(它真的没那么复杂),目前此功能仅作参考,内部实现了最基础的AIPPT生成逻辑,即:模板定制 + AI生成数据与模板结合 + 配图替换。为控制成本暂时只能做到这里,但为了达到生产环节的效果,你还需要做更多,例如更多的模板、更细致的AI工作流程。
+
+注:配图替换仅提供方法,不提供实际演示功能,你需要自己提供图片源接入(如AI文生图、图库搜索匹配等方法)
+
+#### Q. 其他
+
+A. 另外需要强调,PPTist只是一个开源项目而非面向普通用户的产品,主要提供的是技术解决方案,一些产品化的需求/优化还需要开发者自己去实现和完善。
+
+## FAQ
+#### Q. Why doesn’t the xxx shortcut work?
+
+A. Some shortcuts only work when the focus is on a specific area. For example, the shortcuts for operating pages only work when the focus is on the thumbnail list on the left, and the shortcuts for operating elements only work when the focus is on the canvas area.
+
+#### Q. Why isn’t pasting working?
+
+A. Please make sure to allow the browser access to the system clipboard.
+
+#### Q. Why do my previous PPT disappear after refreshing or reopening the browser?
+
+A. The links provided by the repository are for demonstration purposes only, and the project is deployed as a pure front-end application without a backend, thus it does not save data.
+
+#### Q. How do I adjust the order of slides?
+
+A. You can drag and drop the thumbnails on the left to adjust the order.
+
+#### Q. Why does the application become unresponsive after inserting images?
+
+A. Since this demo project does not rely on a backend, inserting local images actually references Base64 encoded data, which can result in very large data sizes. In a real production environment, you should upload images and reference their addresses to avoid this issue.
+
+#### Q. Why doesn’t the preset theme take effect after being applied?
+
+A. Applying a preset theme affects new elements and pages added, but will not apply to existing elements and pages. You can use the “Apply Theme to All” feature to apply the current theme to all pages.
+
+#### Q. Why doesn’t setting an online font work?
+
+A. Setting an online font involves downloading the corresponding font file, which can be large and requires time to complete the download before the new font is applied.
+
+#### Q. About Importing and Exporting PPTX Files
+
+A. As an online presentation application, the ability to import and export PPTX files is very important. However, it has been found that the complexity of implementing this feature far exceeds expectations. Due to limited personal capacity and time, this functionality can only be achieved with the help of third-party solutions.
+
+Export: The current export function is mainly based on [PptxGenJS](https://github.com/gitbrent/PptxGenJS/), and it can export most basic elements, but there are still many defects that need to be improved. It’s important to note that: 1) This feature relies on PptxGenJS, and for parts that the library itself cannot implement (such as animations), there’s nothing this project can do; 2) The goal of the export function is to export elements with styles as consistent as possible, not to recreate the web page one-to-one in PPT, and some style differences are inevitable.
+
+Import: The import function currently does not have a suitable solution and is still under investigation. If you are interested or have experience in related areas, please discuss in the issues.
+
+> PS. I made an experimental [pptx to json](https://github.com/pipipi-pikachu/pptx2json) converter. If you urgently need to implement the import PPTX file function, you can use this as a reference for your own implementation.
+
+It should be noted that this project is not an exclusive online editor for Office PPT. It is essentially unrelated to Office PPT. The [import/export of PPT files] is just a [feature] of the project, not its [purpose].
+
+#### Q. Which video formats are supported?
+
+A. This project only provides basic video capabilities and can play formats supported by the video tag in normal conditions.
+
+Additionally, you can introduce [hls.js](https://github.com/video-dev/hls.js) or [flv.js](https://github.com/Bilibili/flv.js) to support corresponding formats (.m3u8 .flv) by simply including the corresponding files (such as CDN) in your project, without any other configuration required.
+
+#### Q. About Importing JSON Files
+
+A. Firstly, due to security reasons, I do not recommend exposing such functionality directly to users on the front end, or users should not even come into contact with formats like JSON in the first place (even the export JSON feature was initially intended only for development convenience). If there is a real need, please implement it on the server side, with a focus on data validation, and the same goes for the front end.
+
+#### Q. Print / Export PDF Styles Are Different from the Actual
+
+A. Please adjust the settings in the print dialog that pops up in the browser. It is recommended to set the margins to [default], uncheck [headers and footers], and check [background graphics]. Furthermore, it is recommended to adopt a backend-generated PDF solution (such as Puppeteer) for a more optimal outcome in a formal environment.
+
+#### Q. Why doesn’t the mobile version support xxx feature?
+
+A. The first thing to clarify is that the mobile experience will inevitably be inferior to the PC experience no matter what. Therefore, the mobile version is positioned for simple, temporary handling in emergency situations. True design and creation of slides should be done on a computer with full functionality. If there is a specific need for the mobile version, you can try opening it in desktop mode on mobile (of course, the experience will be worse), or the developer can do further custom development.
+
+#### Q. About Compatibility?
+
+A. This project prioritizes compatibility with Chrome and Firefox. There may be some compatibility issues under Safari. It is not compatible with IE.
+
+#### Q. Why isn’t it an NPM package?
+
+A. Everyone knows that for general plugins/libraries, a well-packaged NPM package can more easily integrate into existing projects. However, PPTist is special; it is a complete program, not a part of another program. If you need to use PPTist, I believe you will necessarily need to do a lot of custom development based on it, including but not limited to: communication with the backend, various templates and pre-installed materials, new element types, using other solutions to implement certain existing elements, your own themes, changing shortcuts, and so on… It’s not just about installing something that is the same as the existing demo (which may be convenient but has no practical significance in actual product development). As previously mentioned, there are many things that need to be configurable, and it would be difficult to cater to all these needs if it were an NPM plugin. The development effort would be enormous, and currently, I cannot afford it.
+
+Therefore, the correct way to develop a project using PPTist is to pull the complete code, try to understand it, and modify it to suit your own needs. There are also similar projects in the community, such as [drawio](https://github.com/jgraph/drawio).
+
+#### Q. About AI PPT
+A. I don't want to ride the wave of AI hype, but it's unavoidable as too many people place too much importance on AI. So, I created this DEMO (it's really not that complicated). Currently, this feature is for reference only, and internally, it implements the most basic AI PPT generation logic, which is: template customization + AI-generated data combined with templates + image replacement. To control costs, we can only go this far for now. However, to achieve the effect of a production environment, you would need to do more, such as creating more templates and refining the AI workflow.
+
+Note: Image replacement only provides the method and does not offer an actual demonstration function. You will need to provide your own image sources (such as AI text-to-image generation, image library search matching, etc.).
+
+#### Q. Other
+
+A. Additionally, it is important to emphasize that PPTist is merely an open-source project, not a product tailored for the average user. It primarily offers technical solutions. Some product-oriented demands and optimizations require developers to implement and refine on their own.

+ 2 - 0
env.d.ts

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

+ 63 - 0
index.html

@@ -0,0 +1,63 @@
+<!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 - 在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能,实现在线PPT的编辑、演示,支持导出PPT文件,支持AI生成PPT。" />
+    <meta name="keywords" content="pptist,ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,aippt" />
+    <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%;
+        box-sizing: border-box;
+        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>

+ 9496 - 0
package-lock.json

@@ -0,0 +1,9496 @@
+{
+  "name": "pptist",
+  "version": "2.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "pptist",
+      "version": "2.0.0",
+      "dependencies": {
+        "@icon-park/vue-next": "^1.4.2",
+        "animate.css": "^4.1.1",
+        "axios": "^1.7.9",
+        "clipboard": "^2.0.11",
+        "crypto-js": "^4.2.0",
+        "dexie": "^4.0.11",
+        "echarts": "^5.5.1",
+        "file-saver": "^2.0.5",
+        "hfmath": "^0.0.2",
+        "html-to-image": "^1.11.13",
+        "lodash": "^4.17.21",
+        "mitt": "^3.0.1",
+        "nanoid": "^5.0.7",
+        "number-precision": "^1.6.0",
+        "pinia": "^3.0.2",
+        "pptxgenjs": "^3.12.0",
+        "pptxtojson": "^1.5.2",
+        "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.5.17",
+        "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-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+      "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+      "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.28.0",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz",
+      "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
+      "dependencies": {
+        "@babel/types": "^7.28.0"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.0.tgz",
+      "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=6.9.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.4",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+      "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="
+    },
+    "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.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
+      "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
+      "dependencies": {
+        "@babel/parser": "^7.27.5",
+        "@vue/shared": "3.5.17",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
+      "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
+      "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
+      "dependencies": {
+        "@babel/parser": "^7.27.5",
+        "@vue/compiler-core": "3.5.17",
+        "@vue/compiler-dom": "3.5.17",
+        "@vue/compiler-ssr": "3.5.17",
+        "@vue/shared": "3.5.17",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.17",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
+      "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "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": "7.7.2",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.2.tgz",
+      "integrity": "sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==",
+      "dependencies": {
+        "@vue/devtools-kit": "^7.7.2"
+      }
+    },
+    "node_modules/@vue/devtools-kit": {
+      "version": "7.7.2",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz",
+      "integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==",
+      "dependencies": {
+        "@vue/devtools-shared": "^7.7.2",
+        "birpc": "^0.2.19",
+        "hookable": "^5.5.3",
+        "mitt": "^3.0.1",
+        "perfect-debounce": "^1.0.0",
+        "speakingurl": "^14.0.1",
+        "superjson": "^2.2.1"
+      }
+    },
+    "node_modules/@vue/devtools-shared": {
+      "version": "7.7.2",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz",
+      "integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==",
+      "dependencies": {
+        "rfdc": "^1.4.1"
+      }
+    },
+    "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.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.17.tgz",
+      "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==",
+      "dependencies": {
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.17.tgz",
+      "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz",
+      "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.17",
+        "@vue/runtime-core": "3.5.17",
+        "@vue/shared": "3.5.17",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.17.tgz",
+      "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.17",
+        "@vue/shared": "3.5.17"
+      },
+      "peerDependencies": {
+        "vue": "3.5.17"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.17.tgz",
+      "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg=="
+    },
+    "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/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.7.9",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.9.tgz",
+      "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.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.3.0",
+      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/birpc": {
+      "version": "0.2.19",
+      "resolved": "https://registry.npmmirror.com/birpc/-/birpc-0.2.19.tgz",
+      "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "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.6.0",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "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"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "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/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/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/copy-anything": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
+      "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+      "dependencies": {
+        "is-what": "^4.1.8"
+      },
+      "engines": {
+        "node": ">=12.13"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "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/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "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": "4.0.11",
+      "resolved": "https://registry.npmmirror.com/dexie/-/dexie-4.0.11.tgz",
+      "integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A=="
+    },
+    "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/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz",
+      "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "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/hookable": {
+      "version": "5.5.3",
+      "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
+      "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
+    },
+    "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.13",
+      "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.13.tgz",
+      "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg=="
+    },
+    "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.7",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.7.tgz",
+      "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
+      "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/is-what": {
+      "version": "4.1.16",
+      "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
+      "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
+      "engines": {
+        "node": ">=12.13"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "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.17",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "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/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mimic-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/perfect-debounce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+      "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+    },
+    "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": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.2.tgz",
+      "integrity": "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g==",
+      "dependencies": {
+        "@vue/devtools-api": "^7.7.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.4.4",
+        "vue": "^2.7.0 || ^3.5.11"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "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.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "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.11",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "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.5.2",
+      "resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-1.5.2.tgz",
+      "integrity": "sha512-8kvZUr92qHOBUUchUg1c9hMU/lwcIUEoDLf0wEwoM3FenOfNYRPxlnbPYLcngzvShhRgwAGmp4j/nmCGf3+VMA==",
+      "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/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "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/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
+    },
+    "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.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "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/speakingurl": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
+      "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "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/superjson": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
+      "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
+      "dependencies": {
+        "copy-anything": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "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.5.17",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.17.tgz",
+      "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.17",
+        "@vue/compiler-sfc": "3.5.17",
+        "@vue/runtime-dom": "3.5.17",
+        "@vue/server-renderer": "3.5.17",
+        "@vue/shared": "3.5.17"
+      },
+      "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-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+      "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="
+    },
+    "@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.28.0",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz",
+      "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
+      "requires": {
+        "@babel/types": "^7.28.0"
+      }
+    },
+    "@babel/types": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.0.tgz",
+      "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
+      "requires": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.27.1"
+      }
+    },
+    "@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.4",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+      "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="
+    },
+    "@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.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
+      "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
+      "requires": {
+        "@babel/parser": "^7.27.5",
+        "@vue/shared": "3.5.17",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
+      "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
+      "requires": {
+        "@vue/compiler-core": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
+      "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
+      "requires": {
+        "@babel/parser": "^7.27.5",
+        "@vue/compiler-core": "3.5.17",
+        "@vue/compiler-dom": "3.5.17",
+        "@vue/compiler-ssr": "3.5.17",
+        "@vue/shared": "3.5.17",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.17",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
+      "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
+      "requires": {
+        "@vue/compiler-dom": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "@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": "7.7.2",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.2.tgz",
+      "integrity": "sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==",
+      "requires": {
+        "@vue/devtools-kit": "^7.7.2"
+      }
+    },
+    "@vue/devtools-kit": {
+      "version": "7.7.2",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz",
+      "integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==",
+      "requires": {
+        "@vue/devtools-shared": "^7.7.2",
+        "birpc": "^0.2.19",
+        "hookable": "^5.5.3",
+        "mitt": "^3.0.1",
+        "perfect-debounce": "^1.0.0",
+        "speakingurl": "^14.0.1",
+        "superjson": "^2.2.1"
+      }
+    },
+    "@vue/devtools-shared": {
+      "version": "7.7.2",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz",
+      "integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==",
+      "requires": {
+        "rfdc": "^1.4.1"
+      }
+    },
+    "@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.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.17.tgz",
+      "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==",
+      "requires": {
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.17.tgz",
+      "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==",
+      "requires": {
+        "@vue/reactivity": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz",
+      "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==",
+      "requires": {
+        "@vue/reactivity": "3.5.17",
+        "@vue/runtime-core": "3.5.17",
+        "@vue/shared": "3.5.17",
+        "csstype": "^3.1.3"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.17.tgz",
+      "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==",
+      "requires": {
+        "@vue/compiler-ssr": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.5.17",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.17.tgz",
+      "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg=="
+    },
+    "@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
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "axios": {
+      "version": "1.7.9",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.9.tgz",
+      "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+      "requires": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "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.3.0",
+      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true
+    },
+    "birpc": {
+      "version": "0.2.19",
+      "resolved": "https://registry.npmmirror.com/birpc/-/birpc-0.2.19.tgz",
+      "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ=="
+    },
+    "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.6.0",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "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
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "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"
+      }
+    },
+    "copy-anything": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
+      "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+      "requires": {
+        "is-what": "^4.1.8"
+      }
+    },
+    "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
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+    },
+    "delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
+    "dexie": {
+      "version": "4.0.11",
+      "resolved": "https://registry.npmmirror.com/dexie/-/dexie-4.0.11.tgz",
+      "integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A=="
+    },
+    "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
+    },
+    "follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
+    },
+    "form-data": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz",
+      "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "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=="
+    },
+    "hookable": {
+      "version": "5.5.3",
+      "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
+      "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
+    },
+    "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.13",
+      "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.13.tgz",
+      "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg=="
+    },
+    "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.7",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.7.tgz",
+      "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
+      "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"
+      }
+    },
+    "is-what": {
+      "version": "4.1.16",
+      "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
+      "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="
+    },
+    "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.17",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "requires": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "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"
+      }
+    },
+    "mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "requires": {
+        "mime-db": "1.52.0"
+      }
+    },
+    "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
+    },
+    "perfect-debounce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+      "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="
+    },
+    "picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+    },
+    "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": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.2.tgz",
+      "integrity": "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g==",
+      "requires": {
+        "@vue/devtools-api": "^7.7.2"
+      }
+    },
+    "postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "requires": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "dependencies": {
+        "nanoid": {
+          "version": "3.3.11",
+          "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+          "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="
+        }
+      }
+    },
+    "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.5.2",
+      "resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-1.5.2.tgz",
+      "integrity": "sha512-8kvZUr92qHOBUUchUg1c9hMU/lwcIUEoDLf0wEwoM3FenOfNYRPxlnbPYLcngzvShhRgwAGmp4j/nmCGf3+VMA==",
+      "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"
+      }
+    },
+    "proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "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
+    },
+    "rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
+    },
+    "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.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
+    },
+    "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
+    },
+    "speakingurl": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
+      "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="
+    },
+    "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
+    },
+    "superjson": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
+      "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
+      "requires": {
+        "copy-anything": "^3.0.2"
+      }
+    },
+    "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.5.17",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.17.tgz",
+      "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
+      "requires": {
+        "@vue/compiler-dom": "3.5.17",
+        "@vue/compiler-sfc": "3.5.17",
+        "@vue/runtime-dom": "3.5.17",
+        "@vue/server-renderer": "3.5.17",
+        "@vue/shared": "3.5.17"
+      }
+    },
+    "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"
+      }
+    }
+  }
+}

+ 76 - 0
package.json

@@ -0,0 +1,76 @@
+{
+  "name": "pptist",
+  "version": "2.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"
+  },
+  "author": "pipipi_pikachu@163.com",
+  "homepage": "https://github.com/pipipi-pikachu/PPTist",
+  "dependencies": {
+    "@icon-park/vue-next": "^1.4.2",
+    "animate.css": "^4.1.1",
+    "axios": "^1.7.9",
+    "clipboard": "^2.0.11",
+    "crypto-js": "^4.2.0",
+    "dexie": "^4.0.11",
+    "echarts": "^5.5.1",
+    "file-saver": "^2.0.5",
+    "hfmath": "^0.0.2",
+    "html-to-image": "^1.11.13",
+    "lodash": "^4.17.21",
+    "mitt": "^3.0.1",
+    "nanoid": "^5.0.7",
+    "number-precision": "^1.6.0",
+    "pinia": "^3.0.2",
+    "pptxgenjs": "^3.12.0",
+    "pptxtojson": "^1.5.2",
+    "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.5.17",
+    "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"
+  }
+}

BIN=BIN
public/favicon.ico


BIN=BIN
public/logo.png


+ 475 - 0
public/mocks/AIPPT.json

@@ -0,0 +1,475 @@
+[
+  {
+    "type": "cover",
+    "data": {
+      "title": "犯罪心理学研究",
+      "text": "探索犯罪心理的成因、特征及干预策略,助力犯罪预防与矫治。"
+    }
+  },
+  {
+    "type": "contents",
+    "data": {
+      "items": [
+        "犯罪心理学概述",
+        "犯罪心理的形成",
+        "犯罪类型与心理特征",
+        "犯罪心理评估与干预",
+        "犯罪心理学的应用",
+        "未来发展趋势"
+      ]
+    }
+  },
+  {
+    "type": "transition",
+    "data": {
+      "title": "犯罪心理学概述",
+      "text": "本章将介绍犯罪心理学的定义、历史、基本理论及其研究领域。"
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "定义与历史",
+      "items": [
+        {
+          "title": "犯罪心理学的定义",
+          "text": "犯罪心理学是研究犯罪行为及其心理机制的学科,旨在理解犯罪者的心理特征和行为动机。"
+        },
+        {
+          "title": "犯罪心理学的发展历史",
+          "text": "犯罪心理学起源于19世纪末,随着心理学和法学的发展逐渐形成独立学科。"
+        },
+        {
+          "title": "主要研究领域",
+          "text": "包括犯罪心理成因、犯罪类型分析、犯罪心理评估与干预等。"
+        },
+        {
+          "title": "与其他学科的关系",
+          "text": "犯罪心理学与法学、社会学、生物学等学科密切相关,共同探讨犯罪现象。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "基本理论",
+      "items": [
+        {
+          "title": "生物学理论",
+          "text": "探讨遗传、神经生理等因素对犯罪行为的影响。"
+        },
+        {
+          "title": "心理学理论",
+          "text": "分析人格、认知、情感等心理因素与犯罪行为的关联。"
+        },
+        {
+          "title": "社会学理论",
+          "text": "研究社会环境、文化背景等对犯罪心理的塑造作用。"
+        },
+        {
+          "title": "综合理论",
+          "text": "结合生物学、心理学和社会学视角,全面解释犯罪心理。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "transition",
+    "data": {
+      "title": "犯罪心理的形成",
+      "text": "本章将探讨个体因素、环境因素及心理过程对犯罪心理的影响。"
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "个体因素",
+      "items": [
+        {
+          "title": "遗传因素",
+          "text": "研究基因与犯罪行为之间的潜在联系。"
+        },
+        {
+          "title": "心理特质",
+          "text": "探讨人格特质如攻击性、冲动性与犯罪的关系。"
+        },
+        {
+          "title": "人格障碍",
+          "text": "分析反社会人格障碍等心理疾病对犯罪行为的推动作用。"
+        },
+        {
+          "title": "早期经历",
+          "text": "研究童年创伤、家庭暴力等对犯罪心理的长期影响。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "环境因素",
+      "items": [
+        {
+          "title": "家庭环境",
+          "text": "探讨家庭结构、教育方式对犯罪心理的塑造作用。"
+        },
+        {
+          "title": "社会环境影响",
+          "text": "分析同伴压力、社区环境对犯罪行为的诱导作用。"
+        },
+        {
+          "title": "经济因素",
+          "text": "研究贫困、失业等经济问题与犯罪率的关系。"
+        },
+        {
+          "title": "文化背景",
+          "text": "探讨文化价值观对犯罪心理的影响。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "心理过程",
+      "items": [
+        {
+          "title": "认知过程",
+          "text": "分析犯罪者的思维方式、决策模式及其对行为的影响。"
+        },
+        {
+          "title": "情感过程",
+          "text": "探讨情绪调节、情感缺失与犯罪行为的关系。"
+        },
+        {
+          "title": "行为动机",
+          "text": "研究犯罪者的内在动机,如利益驱动、报复心理等。"
+        },
+        {
+          "title": "决策机制",
+          "text": "分析犯罪者在实施犯罪前的决策过程及其心理机制。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "transition",
+    "data": {
+      "title": "犯罪类型与心理特征",
+      "text": "本章将分析暴力犯罪、财产犯罪及网络犯罪的心理特征。"
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "暴力犯罪",
+      "items": [
+        {
+          "title": "谋杀心理特征",
+          "text": "探讨谋杀者的心理动机,如情感冲突、利益争夺等。"
+        },
+        {
+          "title": "抢劫心理特征",
+          "text": "分析抢劫者的冒险心理及对暴力的依赖。"
+        },
+        {
+          "title": "性犯罪心理特征",
+          "text": "研究性犯罪者的心理扭曲及行为模式。"
+        },
+        {
+          "title": "家庭暴力心理特征",
+          "text": "探讨家庭暴力施暴者的控制欲及情感缺陷。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "财产犯罪",
+      "items": [
+        {
+          "title": "盗窃心理特征",
+          "text": "分析盗窃者的侥幸心理及对物质的渴望。"
+        },
+        {
+          "title": "诈骗心理特征",
+          "text": "探讨诈骗者的欺骗技巧及心理操纵能力。"
+        },
+        {
+          "title": "贪污心理特征",
+          "text": "研究贪污者的权力欲望及道德沦丧。"
+        },
+        {
+          "title": "洗钱心理特征",
+          "text": "分析洗钱者的风险规避心理及法律规避策略。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "网络犯罪",
+      "items": [
+        {
+          "title": "黑客心理特征",
+          "text": "探讨黑客的技术崇拜及对权威的挑战心理。"
+        },
+        {
+          "title": "网络诈骗心理特征",
+          "text": "分析网络诈骗者的伪装能力及心理操纵技巧。"
+        },
+        {
+          "title": "网络暴力心理特征",
+          "text": "研究网络暴力施暴者的匿名心理及攻击性。"
+        },
+        {
+          "title": "网络成瘾与犯罪",
+          "text": "探讨网络成瘾者对虚拟世界的依赖及其与犯罪行为的关联。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "transition",
+    "data": {
+      "title": "犯罪心理评估与干预",
+      "text": "本章将介绍犯罪心理评估方法及干预策略,探讨预防措施。"
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "评估方法",
+      "items": [
+        {
+          "title": "心理测试",
+          "text": "通过标准化测试评估犯罪者的心理状态及人格特征。"
+        },
+        {
+          "title": "行为观察",
+          "text": "观察犯罪者的日常行为,分析其行为模式及心理动机。"
+        },
+        {
+          "title": "访谈技巧",
+          "text": "通过深度访谈了解犯罪者的心理历程及犯罪动机。"
+        },
+        {
+          "title": "案例分析",
+          "text": "通过典型案例分析犯罪者的心理特征及行为规律。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "干预策略",
+      "items": [
+        {
+          "title": "心理治疗",
+          "text": "通过心理咨询和治疗帮助犯罪者纠正心理问题。"
+        },
+        {
+          "title": "行为矫正",
+          "text": "通过行为训练帮助犯罪者改变不良行为模式。"
+        },
+        {
+          "title": "社会支持",
+          "text": "提供社会资源支持,帮助犯罪者重新融入社会。"
+        },
+        {
+          "title": "法律干预",
+          "text": "通过法律手段对犯罪行为进行约束和惩罚。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "预防措施",
+      "items": [
+        {
+          "title": "早期干预",
+          "text": "通过早期心理干预预防潜在犯罪者的行为问题。"
+        },
+        {
+          "title": "教育预防",
+          "text": "通过教育提升公众的法律意识及心理健康水平。"
+        },
+        {
+          "title": "社区预防",
+          "text": "通过社区活动增强社会凝聚力,减少犯罪诱因。"
+        },
+        {
+          "title": "政策预防",
+          "text": "通过政策制定减少社会不平等,降低犯罪率。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "transition",
+    "data": {
+      "title": "犯罪心理学的应用",
+      "text": "本章将探讨犯罪心理学在刑事司法、犯罪预防及研究教育中的应用。"
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "刑事司法系统",
+      "items": [
+        {
+          "title": "犯罪心理画像",
+          "text": "通过心理分析描绘犯罪者的特征,协助案件侦破。"
+        },
+        {
+          "title": "审讯技巧",
+          "text": "运用心理学方法提高审讯效率,获取真实供述。"
+        },
+        {
+          "title": "证人心理学",
+          "text": "研究证人的心理状态及其对证词可信度的影响。"
+        },
+        {
+          "title": "陪审团决策",
+          "text": "分析陪审员的心理过程及其对判决结果的影响。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "犯罪预防与矫治",
+      "items": [
+        {
+          "title": "犯罪预防策略",
+          "text": "通过心理干预和社会支持预防犯罪行为的发生。"
+        },
+        {
+          "title": "监狱心理矫治",
+          "text": "通过心理治疗帮助服刑人员纠正心理问题,降低再犯率。"
+        },
+        {
+          "title": "社区矫正",
+          "text": "通过社区支持帮助犯罪者重新融入社会,减少再犯风险。"
+        },
+        {
+          "title": "再犯预防",
+          "text": "通过心理评估和干预降低犯罪者的再犯可能性。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "研究与教育",
+      "items": [
+        {
+          "title": "犯罪心理学研究",
+          "text": "通过科学研究深化对犯罪心理的理解,推动学科发展。"
+        },
+        {
+          "title": "犯罪心理学教育",
+          "text": "通过教育培养专业人才,提升犯罪心理学的应用水平。"
+        },
+        {
+          "title": "专业培训",
+          "text": "通过培训提升司法人员的心理学知识和技能。"
+        },
+        {
+          "title": "学术交流",
+          "text": "通过学术交流促进犯罪心理学领域的国际合作与发展。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "transition",
+    "data": {
+      "title": "未来发展趋势",
+      "text": "本章将探讨犯罪心理学在技术应用、跨学科合作及伦理挑战方面的未来发展方向。"
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "技术应用",
+      "items": [
+        {
+          "title": "人工智能与犯罪心理学",
+          "text": "利用人工智能技术分析犯罪数据,提升犯罪预测能力。"
+        },
+        {
+          "title": "大数据分析",
+          "text": "通过大数据技术挖掘犯罪行为的规律及心理特征。"
+        },
+        {
+          "title": "虚拟现实技术",
+          "text": "利用虚拟现实技术模拟犯罪场景,研究犯罪者的心理反应。"
+        },
+        {
+          "title": "神经科学研究",
+          "text": "通过神经科学研究探索犯罪行为的生物学基础。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "跨学科合作",
+      "items": [
+        {
+          "title": "心理学与法律",
+          "text": "加强心理学与法学的合作,提升司法系统的科学性。"
+        },
+        {
+          "title": "心理学与医学",
+          "text": "通过心理学与医学的结合,深化对犯罪心理的理解。"
+        },
+        {
+          "title": "心理学与社会学",
+          "text": "通过心理学与社会学的交叉研究,全面分析犯罪现象。"
+        },
+        {
+          "title": "心理学与信息技术",
+          "text": "结合心理学与信息技术,开发犯罪预防与干预的新工具。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "content",
+    "data": {
+      "title": "伦理与挑战",
+      "items": [
+        {
+          "title": "伦理问题",
+          "text": "探讨犯罪心理学研究中的伦理困境及解决方案。"
+        },
+        {
+          "title": "隐私保护",
+          "text": "研究犯罪心理学应用中如何保护个人隐私。"
+        },
+        {
+          "title": "数据安全",
+          "text": "分析犯罪心理学数据的安全风险及防护措施。"
+        },
+        {
+          "title": "未来挑战",
+          "text": "探讨犯罪心理学在未来发展中可能面临的技术与社会挑战。"
+        }
+      ]
+    }
+  },
+  {
+    "type": "end"
+  }
+]

+ 69 - 0
public/mocks/AIPPT_Outline.md

@@ -0,0 +1,69 @@
+# 5G技术如何改变我们的生活
+## 5G技术概述
+### 5G的定义
+- 第五代移动通信技术
+- 基于4G技术的重大升级
+### 5G的关键特性
+- 高速率
+- 低时延
+- 大容量
+### 5G的发展历程
+- 早期研究阶段
+- 标准制定阶段
+- 商用推广阶段
+
+## 5G对通信领域的变革
+### 个人通信体验提升
+- 高清视频通话无卡顿
+- 快速下载大文件
+- 多人在线游戏低延迟
+### 通信网络架构优化
+- 网络切片技术实现差异化服务
+- 边缘计算减少数据传输距离
+### 通信安全保障增强
+- 新的加密算法保障数据安全
+- 实时监测防范网络攻击
+
+## 5G与智能家居的融合
+### 智能家电控制
+- 远程控制家电开关和运行模式
+- 家电之间智能联动
+### 家庭安防升级
+- 高清实时监控家庭情况
+- 异常情况及时报警
+### 家居环境智能调节
+- 自动调节室内温度、湿度
+- 智能灯光控制营造氛围
+
+## 5G推动智能交通发展
+### 自动驾驶汽车
+- 车辆间实时通信避免碰撞
+- 高精度地图实时更新
+### 智能交通管理
+- 实时监控交通流量并优化信号灯
+- 快速处理交通事故
+### 公共交通智能化
+- 实时公交信息查询
+- 车内高速网络服务
+
+## 5G在医疗领域的应用
+### 远程医疗服务
+- 专家远程诊断病情
+- 远程手术指导
+### 医疗设备互联
+- 可穿戴设备实时传输健康数据
+- 医院内部设备信息共享
+### 智能医疗管理
+- 电子病历快速调取
+- 医疗资源智能分配
+
+## 5G助力工业互联网升级
+### 智能制造
+- 生产设备实时监控和远程维护
+- 柔性生产线智能调度
+### 工业物流优化
+- 货物实时定位和跟踪
+- 智能仓储管理
+### 工业安全保障
+- 危险区域实时监测
+- 事故预警和应急处理 

+ 482 - 0
public/mocks/imgs.json

@@ -0,0 +1,482 @@
+[
+  {
+      "id": 32664236,
+      "width": 3456,
+      "height": 4608,
+      "src": "https://images.pexels.com/photos/32664236/pexels-photo-32664236.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32677160,
+      "width": 3339,
+      "height": 4674,
+      "src": "https://images.pexels.com/photos/32677160/pexels-photo-32677160.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32683858,
+      "width": 3447,
+      "height": 5164,
+      "src": "https://images.pexels.com/photos/32683858/pexels-photo-32683858.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32668972,
+      "width": 2875,
+      "height": 3833,
+      "src": "https://images.pexels.com/photos/32668972/pexels-photo-32668972.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32676050,
+      "width": 4216,
+      "height": 2816,
+      "src": "https://images.pexels.com/photos/32676050/pexels-photo-32676050.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32661200,
+      "width": 6000,
+      "height": 4000,
+      "src": "https://images.pexels.com/photos/32661200/pexels-photo-32661200.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32649630,
+      "width": 4797,
+      "height": 3264,
+      "src": "https://images.pexels.com/photos/32649630/pexels-photo-32649630.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32644651,
+      "width": 2640,
+      "height": 3960,
+      "src": "https://images.pexels.com/photos/32644651/pexels-photo-32644651.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32644842,
+      "width": 2640,
+      "height": 3960,
+      "src": "https://images.pexels.com/photos/32644842/pexels-photo-32644842.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32640655,
+      "width": 3648,
+      "height": 5472,
+      "src": "https://images.pexels.com/photos/32640655/pexels-photo-32640655.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32636738,
+      "width": 1732,
+      "height": 2618,
+      "src": "https://images.pexels.com/photos/32636738/pexels-photo-32636738.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32632253,
+      "width": 4094,
+      "height": 6142,
+      "src": "https://images.pexels.com/photos/32632253/pexels-photo-32632253.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32633540,
+      "width": 6240,
+      "height": 4160,
+      "src": "https://images.pexels.com/photos/32633540/pexels-photo-32633540.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32630763,
+      "width": 5464,
+      "height": 8192,
+      "src": "https://images.pexels.com/photos/32630763/pexels-photo-32630763.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32622786,
+      "width": 5616,
+      "height": 3744,
+      "src": "https://images.pexels.com/photos/32622786/pexels-photo-32622786.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32615328,
+      "width": 4032,
+      "height": 3024,
+      "src": "https://images.pexels.com/photos/32615328/pexels-photo-32615328.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32608898,
+      "width": 3866,
+      "height": 5799,
+      "src": "https://images.pexels.com/photos/32608898/pexels-photo-32608898.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32612572,
+      "width": 3606,
+      "height": 5410,
+      "src": "https://images.pexels.com/photos/32612572/pexels-photo-32612572.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32607286,
+      "width": 4024,
+      "height": 6048,
+      "src": "https://images.pexels.com/photos/32607286/pexels-photo-32607286.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32604325,
+      "width": 4928,
+      "height": 3264,
+      "src": "https://images.pexels.com/photos/32604325/pexels-photo-32604325.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32589762,
+      "width": 4455,
+      "height": 6593,
+      "src": "https://images.pexels.com/photos/32589762/pexels-photo-32589762.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32595999,
+      "width": 5304,
+      "height": 7072,
+      "src": "https://images.pexels.com/photos/32595999/pexels-photo-32595999.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32590595,
+      "width": 5206,
+      "height": 3471,
+      "src": "https://images.pexels.com/photos/32590595/pexels-photo-32590595.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32585052,
+      "width": 3072,
+      "height": 4096,
+      "src": "https://images.pexels.com/photos/32585052/pexels-photo-32585052.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32555471,
+      "width": 2756,
+      "height": 4134,
+      "src": "https://images.pexels.com/photos/32555471/pexels-photo-32555471.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32552462,
+      "width": 4024,
+      "height": 6048,
+      "src": "https://images.pexels.com/photos/32552462/pexels-photo-32552462.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32578129,
+      "width": 2560,
+      "height": 3840,
+      "src": "https://images.pexels.com/photos/32578129/pexels-photo-32578129.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32577893,
+      "width": 2545,
+      "height": 3563,
+      "src": "https://images.pexels.com/photos/32577893/pexels-photo-32577893.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32575970,
+      "width": 4160,
+      "height": 6240,
+      "src": "https://images.pexels.com/photos/32575970/pexels-photo-32575970.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32573374,
+      "width": 3979,
+      "height": 5968,
+      "src": "https://images.pexels.com/photos/32573374/pexels-photo-32573374.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32573408,
+      "width": 2509,
+      "height": 3763,
+      "src": "https://images.pexels.com/photos/32573408/pexels-photo-32573408.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32546608,
+      "width": 6240,
+      "height": 4160,
+      "src": "https://images.pexels.com/photos/32546608/pexels-photo-32546608.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32560592,
+      "width": 2781,
+      "height": 1855,
+      "src": "https://images.pexels.com/photos/32560592/pexels-photo-32560592.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32560517,
+      "width": 4160,
+      "height": 5200,
+      "src": "https://images.pexels.com/photos/32560517/pexels-photo-32560517.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32551016,
+      "width": 3072,
+      "height": 4608,
+      "src": "https://images.pexels.com/photos/32551016/pexels-photo-32551016.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32550977,
+      "width": 3072,
+      "height": 4608,
+      "src": "https://images.pexels.com/photos/32550977/pexels-photo-32550977.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32550946,
+      "width": 3072,
+      "height": 4608,
+      "src": "https://images.pexels.com/photos/32550946/pexels-photo-32550946.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32555845,
+      "width": 3648,
+      "height": 5472,
+      "src": "https://images.pexels.com/photos/32555845/pexels-photo-32555845.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32551624,
+      "width": 2560,
+      "height": 3840,
+      "src": "https://images.pexels.com/photos/32551624/pexels-photo-32551624.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32549559,
+      "width": 4000,
+      "height": 6000,
+      "src": "https://images.pexels.com/photos/32549559/pexels-photo-32549559.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32549562,
+      "width": 4000,
+      "height": 6000,
+      "src": "https://images.pexels.com/photos/32549562/pexels-photo-32549562.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32547348,
+      "width": 3456,
+      "height": 5184,
+      "src": "https://images.pexels.com/photos/32547348/pexels-photo-32547348.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32549629,
+      "width": 3619,
+      "height": 5429,
+      "src": "https://images.pexels.com/photos/32549629/pexels-photo-32549629.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32545595,
+      "width": 6720,
+      "height": 4480,
+      "src": "https://images.pexels.com/photos/32545595/pexels-photo-32545595.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32541889,
+      "width": 4000,
+      "height": 6000,
+      "src": "https://images.pexels.com/photos/32541889/pexels-photo-32541889.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32539060,
+      "width": 4160,
+      "height": 6240,
+      "src": "https://images.pexels.com/photos/32539060/pexels-photo-32539060.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32534656,
+      "width": 3840,
+      "height": 5120,
+      "src": "https://images.pexels.com/photos/32534656/pexels-photo-32534656.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32532135,
+      "width": 4138,
+      "height": 6207,
+      "src": "https://images.pexels.com/photos/32532135/pexels-photo-32532135.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32540341,
+      "width": 4000,
+      "height": 6000,
+      "src": "https://images.pexels.com/photos/32540341/pexels-photo-32540341.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32529049,
+      "width": 4284,
+      "height": 5712,
+      "src": "https://images.pexels.com/photos/32529049/pexels-photo-32529049.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32533642,
+      "width": 2714,
+      "height": 4159,
+      "src": "https://images.pexels.com/photos/32533642/pexels-photo-32533642.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32532634,
+      "width": 4000,
+      "height": 6000,
+      "src": "https://images.pexels.com/photos/32532634/pexels-photo-32532634.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32525175,
+      "width": 1665,
+      "height": 2960,
+      "src": "https://images.pexels.com/photos/32525175/pexels-photo-32525175.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 31916100,
+      "width": 3649,
+      "height": 5362,
+      "src": "https://images.pexels.com/photos/31916100/pexels-photo-31916100.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32521290,
+      "width": 3000,
+      "height": 3750,
+      "src": "https://images.pexels.com/photos/32521290/pexels-photo-32521290.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32520688,
+      "width": 6000,
+      "height": 3376,
+      "src": "https://images.pexels.com/photos/32520688/pexels-photo-32520688.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32519470,
+      "width": 4672,
+      "height": 7008,
+      "src": "https://images.pexels.com/photos/32519470/pexels-photo-32519470.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32515311,
+      "width": 10920,
+      "height": 14820,
+      "src": "https://images.pexels.com/photos/32515311/pexels-photo-32515311.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32503669,
+      "width": 3024,
+      "height": 4032,
+      "src": "https://images.pexels.com/photos/32503669/pexels-photo-32503669.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32508417,
+      "width": 3120,
+      "height": 4680,
+      "src": "https://images.pexels.com/photos/32508417/pexels-photo-32508417.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32508677,
+      "width": 2576,
+      "height": 3864,
+      "src": "https://images.pexels.com/photos/32508677/pexels-photo-32508677.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32507916,
+      "width": 5152,
+      "height": 7728,
+      "src": "https://images.pexels.com/photos/32507916/pexels-photo-32507916.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32505093,
+      "width": 6000,
+      "height": 4000,
+      "src": "https://images.pexels.com/photos/32505093/pexels-photo-32505093.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32504045,
+      "width": 4556,
+      "height": 6826,
+      "src": "https://images.pexels.com/photos/32504045/pexels-photo-32504045.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32446182,
+      "width": 3951,
+      "height": 3951,
+      "src": "https://images.pexels.com/photos/32446182/pexels-photo-32446182.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32470472,
+      "width": 3833,
+      "height": 5749,
+      "src": "https://images.pexels.com/photos/32470472/pexels-photo-32470472.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32489808,
+      "width": 3939,
+      "height": 5909,
+      "src": "https://images.pexels.com/photos/32489808/pexels-photo-32489808.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32496034,
+      "width": 3024,
+      "height": 4032,
+      "src": "https://images.pexels.com/photos/32496034/pexels-photo-32496034.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32495601,
+      "width": 5152,
+      "height": 7728,
+      "src": "https://images.pexels.com/photos/32495601/pexels-photo-32495601.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32487360,
+      "width": 4284,
+      "height": 5712,
+      "src": "https://images.pexels.com/photos/32487360/pexels-photo-32487360.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32438210,
+      "width": 3631,
+      "height": 2433,
+      "src": "https://images.pexels.com/photos/32438210/pexels-photo-32438210.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32470441,
+      "width": 1759,
+      "height": 2346,
+      "src": "https://images.pexels.com/photos/32470441/pexels-photo-32470441.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 28920733,
+      "width": 3024,
+      "height": 4032,
+      "src": "https://images.pexels.com/photos/28920733/pexels-photo-28920733.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32489970,
+      "width": 3469,
+      "height": 5202,
+      "src": "https://images.pexels.com/photos/32489970/pexels-photo-32489970.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32488279,
+      "width": 3673,
+      "height": 5509,
+      "src": "https://images.pexels.com/photos/32488279/pexels-photo-32488279.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32487718,
+      "width": 4284,
+      "height": 5712,
+      "src": "https://images.pexels.com/photos/32487718/pexels-photo-32487718.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32486469,
+      "width": 4284,
+      "height": 5712,
+      "src": "https://images.pexels.com/photos/32486469/pexels-photo-32486469.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32475260,
+      "width": 3458,
+      "height": 4610,
+      "src": "https://images.pexels.com/photos/32475260/pexels-photo-32475260.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32474033,
+      "width": 9504,
+      "height": 6336,
+      "src": "https://images.pexels.com/photos/32474033/pexels-photo-32474033.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  },
+  {
+      "id": 32474034,
+      "width": 6336,
+      "height": 9504,
+      "src": "https://images.pexels.com/photos/32474034/pexels-photo-32474034.jpeg?auto=compress&cs=tinysrgb&h=650&w=940"
+  }
+]

+ 184 - 0
public/mocks/slides.json

@@ -0,0 +1,184 @@
+[
+  {
+    "id": "test-slide-1",
+    "elements": [
+      {
+        "type": "shape",
+        "id": "4cbRxp",
+        "left": 0,
+        "top": 200,
+        "width": 546,
+        "height": 362.5,
+        "viewBox": [200, 200],
+        "path": "M 0 0 L 0 200 L 200 200 Z",
+        "fill": "#5b9bd5",
+        "fixedRatio": false,
+        "opacity": 0.7,
+        "rotate": 0
+      },
+      {
+        "type": "shape",
+        "id": "ookHrf",
+        "left": 0,
+        "top": 0,
+        "width": 300,
+        "height": 320,
+        "viewBox": [200, 200],
+        "path": "M 0 0 L 0 200 L 200 200 Z",
+        "fill": "#5b9bd5",
+        "fixedRatio": false,
+        "flipV": true,
+        "rotate": 0
+      },
+      {
+        "type": "text",
+        "id": "idn7Mx",
+        "left": 355,
+        "top": 65.25,
+        "width": 450,
+        "height": 188,
+        "lineHeight": 1.2,
+        "content": "<p><strong><span style=\"font-size: 112px;\">PPTist</span></strong></p>",
+        "rotate": 0,
+        "defaultFontName": "",
+        "defaultColor": "#333"
+      },
+      {
+        "type": "text",
+        "id": "7stmVP",
+        "left": 355,
+        "top": 253.25,
+        "width": 585,
+        "height": 56,
+        "content": "<p><span style=\"font-size: 24px;\">基于 Vue 3.x + TypeScript 的在线演示文稿应用</span></p>",
+        "rotate": 0,
+        "defaultFontName": "",
+        "defaultColor": "#333"
+      },
+      {
+        "type": "line",
+        "id": "FnpZs4",
+        "left": 361,
+        "top": 238,
+        "start": [0, 0],
+        "end": [549, 0],
+        "points": ["", ""],
+        "color": "#5b9bd5",
+        "style": "solid",
+        "width": 2
+      }
+    ],
+    "background": {
+      "type": "solid",
+      "color": "#ffffff"
+    }
+  },
+  {
+    "id": "test-slide-2",
+    "elements": [
+      {
+        "type": "text",
+        "id": "ptNnUJ",
+        "left": 145,
+        "top": 148,
+        "width": 711,
+        "height": 77,
+        "lineHeight": 1.2,
+        "content": "<p style=\"text-align: center;\"><strong><span style=\"font-size: 48px;\">在此处添加标题</span></strong></p>",
+        "rotate": 0,
+        "defaultFontName": "",
+        "defaultColor": "#333"
+      },
+      {
+        "type": "text",
+        "id": "mRHvQN",
+        "left": 207.50000000000003,
+        "top": 249.84259259259264,
+        "width": 585,
+        "height": 56,
+        "content": "<p style=\"text-align: center;\"><span style=\"font-size: 24px;\">在此处添加副标题</span></p>",
+        "rotate": 0,
+        "defaultFontName": "",
+        "defaultColor": "#333"
+      },
+      {
+        "type": "line",
+        "id": "7CQDwc",
+        "left": 323.09259259259267,
+        "top": 238.33333333333334,
+        "start": [0, 0],
+        "end": [354.8148148148148, 0],
+        "points": ["", ""],
+        "color": "#5b9bd5",
+        "style": "solid",
+        "width": 4
+      },
+      {
+        "type": "shape",
+        "id": "09wqWw",
+        "left": -27.648148148148138,
+        "top": 432.73148148148147,
+        "width": 1056.2962962962963,
+        "height": 162.96296296296296,
+        "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",
+        "fill": "#5b9bd5",
+        "fixedRatio": false,
+        "rotate": 0
+      }
+    ],
+    "background": {
+      "type": "solid",
+      "color": "#fff"
+    }
+  },
+  {
+    "id": "test-slide-3",
+    "elements": [
+      {
+        "type": "shape",
+        "id": "vSheCJ",
+        "left": 183.5185185185185,
+        "top": 175.5092592592593,
+        "width": 605.1851851851851,
+        "height": 185.18518518518516,
+        "viewBox": [200, 200],
+        "path": "M 0 0 L 200 0 L 200 200 L 0 200 Z",
+        "fill": "#5b9bd5",
+        "fixedRatio": false,
+        "rotate": 0
+      },
+      {
+        "type": "shape",
+        "id": "Mpwv7x",
+        "left": 211.29629629629628,
+        "top": 201.80555555555557,
+        "width": 605.1851851851851,
+        "height": 185.18518518518516,
+        "viewBox": [200, 200],
+        "path": "M 0 0 L 200 0 L 200 200 L 0 200 Z",
+        "fill": "#5b9bd5",
+        "fixedRatio": false,
+        "rotate": 0,
+        "opacity": 0.7
+      },
+      {
+        "type": "text",
+        "id": "WQOTAp",
+        "left": 304.9074074074074,
+        "top": 198.10185185185182,
+        "width": 417.9629629629629,
+        "height": 140,
+        "content": "<p style=\"text-align: center;\"><strong><span style=\"font-size: 80px;\"><span style=\"color: rgb(255, 255, 255);\">感谢观看</span></span></strong></p>",
+        "rotate": 0,
+        "defaultFontName": "",
+        "defaultColor": "#333",
+        "wordSpace": 5
+      }
+    ],
+    "background": {
+      "type": "solid",
+      "color": "#fff"
+    }
+  }
+]

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
public/mocks/template_1.json


+ 63 - 0
src/App.vue

@@ -0,0 +1,63 @@
+<template>
+  <template v-if="slides.length">
+    <Screen v-if="screening" />
+    <Editor v-else-if="_isPC" />
+    <Mobile v-else />
+  </template>
+  <FullscreenSpin tip="数据初始化中,请稍等 ..." v-else  loading :mask="false" />
+</template>
+
+
+
+<script lang="ts" setup>
+import { onMounted } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useScreenStore, useMainStore, useSnapshotStore, useSlidesStore } from '@/store'
+import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
+import { deleteDiscardedDB } from '@/utils/database'
+import { isPC } from '@/utils/common'
+import api from '@/services'
+
+import Editor from './views/Editor/index.vue'
+import Screen from './views/Screen/index.vue'
+import Mobile from './views/Mobile/index.vue'
+import FullscreenSpin from '@/components/FullscreenSpin.vue'
+
+const _isPC = isPC()
+
+const mainStore = useMainStore()
+const slidesStore = useSlidesStore()
+const snapshotStore = useSnapshotStore()
+const { databaseId } = storeToRefs(mainStore)
+const { slides } = storeToRefs(slidesStore)
+const { screening } = storeToRefs(useScreenStore())
+
+if (import.meta.env.MODE !== 'development') {
+  window.onbeforeunload = () => false
+}
+
+onMounted(async () => {
+  const slides = await api.getFileData('slides')
+  slidesStore.setSlides(slides)
+
+  await deleteDiscardedDB()
+  snapshotStore.initSnapshotDatabase()
+})
+
+// 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
+window.addEventListener('beforeunload', () => {
+  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=BIN
src/assets/fonts/AlibabaPuHuiTi.woff2


BIN=BIN
src/assets/fonts/CangerXiaowanzi.woff2


BIN=BIN
src/assets/fonts/DeYiHei.woff2


BIN=BIN
src/assets/fonts/FangZhengFangSong.woff2


BIN=BIN
src/assets/fonts/FangZhengHeiTi.woff2


BIN=BIN
src/assets/fonts/FangZhengKaiTi.woff2


BIN=BIN
src/assets/fonts/FangZhengShuSong.woff2


BIN=BIN
src/assets/fonts/FengguangMingrui.woff2


BIN=BIN
src/assets/fonts/LXGWWenKai.woff2


BIN=BIN
src/assets/fonts/MiSans.woff2


BIN=BIN
src/assets/fonts/RuiziZhenyan.woff2


BIN=BIN
src/assets/fonts/ShetuModernSquare.woff2


BIN=BIN
src/assets/fonts/SourceHanSans.woff2


BIN=BIN
src/assets/fonts/SourceHanSerif.woff2


BIN=BIN
src/assets/fonts/SucaiJishiCoolSquare.woff2


BIN=BIN
src/assets/fonts/SucaiJishiKangkang.woff2


BIN=BIN
src/assets/fonts/TuniuRounded.woff2


BIN=BIN
src/assets/fonts/WenDingPLKaiTi.woff2


BIN=BIN
src/assets/fonts/YousheTitleBlack.woff2


BIN=BIN
src/assets/fonts/ZcoolHappy.woff2


BIN=BIN
src/assets/fonts/ZhuQueFangSong.woff2


BIN=BIN
src/assets/fonts/ZizhiQuXiMai.woff2


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

@@ -0,0 +1,9 @@
+$fonts: 'SourceHanSans', 'SourceHanSerif', 'FangZhengHeiTi', 'FangZhengKaiTi', 'FangZhengShuSong', 'FangZhengFangSong', 'AlibabaPuHuiTi', 'ZhuQueFangSong', 'LXGWWenKai', 'WenDingPLKaiTi', 'DeYiHei', 'MiSans', 'CangerXiaowanzi', 'YousheTitleBlack', 'FengguangMingrui', 'ShetuModernSquare', 'ZcoolHappy', 'ZizhiQuXiMai', 'SucaiJishiKangkang', 'SucaiJishiCoolSquare', 'TuniuRounded', 'RuiziZhenyan';
+
+@each $font in $fonts {
+  @font-face {
+    font-display: swap;
+    font-family: $font;
+    src: url('https://asset.pptist.cn/font/#{$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;
+}

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

@@ -0,0 +1,102 @@
+.ProseMirror, .ProseMirror-static {
+  outline: 0;
+  border: 0;
+  font-size: 16px;
+  word-break: break-word;
+  white-space: normal;
+
+  &:not(.ProseMirror-static) {
+    user-select: text;
+  }
+
+  ::selection {
+    background-color: rgba($themeColor, 0.25);
+    color: inherit;
+  }
+
+  p {
+    margin: 0;
+    margin-top: var(--paragraphSpace);
+  }
+  p:first-child {
+    margin-top: 0;
+  }
+
+  ul, ol, li {
+    margin: 0;
+    margin-top: var(--paragraphSpace);
+  }
+  ul {
+    list-style-type: disc;
+    padding-inline-start: 1.25em;
+
+    li {
+      list-style-type: inherit;
+      padding: 0.125em 0;
+    }
+  }
+
+  ol {
+    list-style-type: decimal;
+    padding-inline-start: 1.25em;
+
+    li {
+      list-style-type: inherit;
+      padding: 0.125em 0;
+    }
+  }
+
+  code {
+    background-color: #f1f1f1;
+    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: 0 1.2em;
+    margin: 0.6em 0;
+    font-style: italic;
+    border-left: 4px solid #e0e0e0;
+  }
+
+  [data-indent='1'] {
+    padding-left: 1em;
+  }
+  [data-indent='2'] {
+    padding-left: 2em;
+  }
+  [data-indent='3'] {
+    padding-left: 3em;
+  }
+  [data-indent='4'] {
+    padding-left: 4em;
+  }
+  [data-indent='5'] {
+    padding-left: 5em;
+  }
+  [data-indent='6'] {
+    padding-left: 6em;
+  }
+  [data-indent='7'] {
+    padding-left: 7em;
+  }
+  [data-indent='8'] {
+    padding-left: 8em;
+  }
+}
+
+.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>

+ 58 - 0
src/components/ColorListButton.vue

@@ -0,0 +1,58 @@
+<template>
+  <Button class="color-btn">
+    <div class="blocks">
+      <div class="color-block" v-for="(color, index) in colors" :key="index">
+        <div class="content" :style="{ backgroundColor: color }"></div>
+      </div>
+    </div>
+    <IconPlatte class="color-btn-icon" />
+  </Button>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import Button from './Button.vue'
+
+const props = defineProps<{
+  colors: string[]
+}>()
+
+const colors = computed(() => {
+  if (props.colors.length > 12) return props.colors.slice(0, 12)
+  return props.colors
+})
+</script>
+
+<style lang="scss" scoped>
+.color-btn {
+  width: 100%;
+  display: flex !important;
+  align-items: center;
+  justify-content: center;
+  padding: 0 !important;
+}
+.blocks {
+  display: flex;
+  flex: 1;
+  margin-left: 8px;
+  outline: 1px dashed rgba($color: #666, $alpha: .12);
+}
+.color-block {
+  height: 20px;
+  flex: 1;
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAEBJREFUOE9jfPbs2X8GIoCkpCQRqhgYGEcNxBlOo2GIM2iGQLL5//8/UTnl+fPnxOWUUQNxhtNoGOLOKYM+2QAAh2Nq10DwkukAAAAASUVORK5CYII=);
+
+  & + & {
+    margin-left: 2px;
+  }
+}
+.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, useTemplateRef } 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 = useTemplateRef<HTMLElement>('alphaRef')
+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, useTemplateRef } 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 = useTemplateRef<HTMLElement>('hueRef')
+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, useTemplateRef } 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 = useTemplateRef<HTMLElement>('saturationRef')
+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>

+ 46 - 0
src/components/FileInput.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="file-input" @click="handleClick()">
+    <slot></slot>
+    <input 
+      class="input"
+      type="file" 
+      name="upload" 
+      ref="inputRef" 
+      :accept="accept" 
+      @change="$event => handleChange($event)"
+      @click.stop
+    >
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useTemplateRef } from 'vue'
+
+withDefaults(defineProps<{
+  accept?: string
+}>(), {
+  accept: 'image/*',
+})
+
+const emit = defineEmits<{
+  (event: 'change', payload: FileList): void
+}>()
+
+const inputRef = useTemplateRef<HTMLInputElement>('inputRef')
+
+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>

+ 71 - 0
src/components/FullscreenSpin.vue

@@ -0,0 +1,71 @@
+<template>
+  <div class="fullscreen-spin" :class="{ 'mask': mask }" 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
+  mask?: boolean
+  tip?: string
+}>(), {
+  loading: false,
+  mask: true,
+  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;
+
+  &.mask {
+    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>

+ 149 - 0
src/components/GradientBar.vue

@@ -0,0 +1,149 @@
+<template>
+  <div class="gradient-bar">
+    <div class="bar" ref="barRef" :style="{ backgroundImage: gradientStyle }" @click="$event => addPoint($event)"></div>
+    <div class="point" 
+      :class="{ 'active': index === i }"
+      v-for="(item, i) in points" 
+      :key="item.pos + '-' + i" 
+      :style="{
+        backgroundColor: item.color,
+        left: `calc(${item.pos}% - 5px)`,
+      }"
+      @mousedown.left="movePoint(i)"
+      @click.right="removePoint(i)"
+    ></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed, watchEffect, useTemplateRef } from 'vue'
+import type { GradientColor } from '@/types/slides'
+
+const props = defineProps<{
+  value: GradientColor[]
+  index: number
+}>()
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: GradientColor[]): void
+  (event: 'update:index', payload: number): void
+}>()
+
+const points = ref<GradientColor[]>([])
+
+const barRef = useTemplateRef<HTMLElement>('barRef')
+
+watchEffect(() => {
+  points.value = props.value
+  if (props.index > props.value.length - 1) emit('update:index', 0)
+})
+
+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
+
+  let targetIndex = 0
+
+  if (index === props.index) {
+    targetIndex = (index - 1 < 0) ? 0 : index - 1
+  }
+  else if (props.index === props.value.length - 1) {
+    targetIndex = props.value.length - 2
+  }
+
+  const values = props.value.filter((item, _index) => _index !== index)
+  emit('update:index', targetIndex)
+  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
+    }
+
+    _points.splice(targetIndex, 0, point)
+
+    emit('update:index', targetIndex)
+    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 })
+  emit('update:index', 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>

+ 136 - 0
src/components/Input.vue

@@ -0,0 +1,136 @@
+<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"
+      :maxlength="maxlength"
+      @input="$event => handleInput($event)"
+      @focus="$event => handleFocus($event)"
+      @blur="$event => handleBlur($event)"
+      @change="$event => emit('change', $event)"
+      @keydown.enter="$event => emit('enter', $event)"
+      @keydown.backspace="$event => emit('backspace', $event)"
+    />
+    <span class="suffix">
+      <slot name="suffix"></slot>
+    </span>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useTemplateRef, ref } from 'vue'
+
+withDefaults(defineProps<{
+  value: string
+  disabled?: boolean
+  placeholder?: string
+  simple?: boolean
+  maxlength?: number
+}>(), {
+  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
+  (event: 'backspace', 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 = useTemplateRef<HTMLInputElement>('inputRef')
+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, useTemplateRef } 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 = useTemplateRef<InstanceType<typeof TextArea>>('textAreaRef')
+
+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, useTemplateRef, 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 = useTemplateRef<HTMLDivElement>('modalRef')
+
+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, useTemplateRef } 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 = useTemplateRef<HTMLElement>('moveablePanelRef')
+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 - (props.height || 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>

+ 350 - 0
src/components/OutlineEditor.vue

@@ -0,0 +1,350 @@
+<template>
+  <div class="outline-editor">
+    <div class="item" 
+      :class="[{ 'title': item.title }, `lv-${item.lv}`]"
+      v-for="item in data"
+      :key="item.id"
+      :data-lv="item.lv"
+      :data-id="item.id"
+      v-contextmenu="contextmenus" 
+    >
+      <Input 
+        class="editable-text" 
+        :value="item.content" 
+        v-if="activeItemId === item.id" 
+        @blur="$event => handleBlur($event, item)"
+        @enter="$event => handleEnter($event, item)"
+        @backspace="$event => handleBackspace($event, item)"
+      />
+      <div class="text" @click="handleFocus(item.id)" v-else>{{ item.content }}</div>
+
+      <div class="flag"></div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, nextTick, onMounted, watch } from 'vue'
+import { nanoid } from 'nanoid'
+import type { ContextmenuItem } from '@/components/Contextmenu/types'
+import Input from './Input.vue'
+
+interface OutlineItem {
+  id: string
+  content: string
+  lv: number
+  title?: boolean
+}
+
+const props = defineProps<{
+  value: string
+}>()
+
+const emit = defineEmits<{
+  (event: 'update:value', payload: string): void
+}>()
+
+const data = ref<OutlineItem[]>([])
+const activeItemId = ref('')
+
+watch(data, () => {
+  let markdown = ''
+  const prefixTitle = '#'
+  const prefixItem = '-'
+  for (const item of data.value) {
+    if (item.lv !== 1) markdown += '\n'
+    if (item.title) markdown += `${prefixTitle.repeat(item.lv)} ${item.content}`
+    else markdown += `${prefixItem} ${item.content}`
+  }
+  emit('update:value', markdown)
+})
+
+onMounted(() => {
+  const lines = props.value.split('\n')
+  const result: OutlineItem[] = []
+
+  for (const line of lines) {
+    if (!line.trim()) continue
+
+    const headerMatch = line.match(/^(#+)\s*(.*)/)
+    const listMatch = line.match(/^-\s*(.*)/)
+
+    if (headerMatch) {
+      const lv = headerMatch[1].length
+      const content = headerMatch[2]
+      result.push({
+        id: nanoid(),
+        content,
+        title: true,
+        lv,
+      })
+    }
+    else if (listMatch) {
+      const content = listMatch[1]
+      result.push({
+        id: nanoid(),
+        content,
+        lv: 4,
+      })
+    }
+    else {
+      result.push({
+        id: nanoid(),
+        content: line.trim(),
+        lv: 4
+      })
+    }
+  }
+  data.value = result
+})
+
+const handleFocus = (id: string) => {
+  activeItemId.value = id
+
+  nextTick(() => {
+    const editableRef = document.querySelector('.editable-text input') as HTMLInputElement
+    editableRef.focus()
+  })
+}
+
+const handleBlur = (e: Event, item: OutlineItem) => {
+  activeItemId.value = ''
+  const value = (e.target as HTMLInputElement).value
+  data.value = data.value.map(_item => {
+    if (_item.id === item.id) return { ..._item, content: value }
+    return _item
+  })
+}
+
+const handleEnter = (e: Event, item: OutlineItem) => {
+  const value = (e.target as HTMLInputElement).value
+  if (!value) return
+
+  activeItemId.value = ''
+
+  if (!item.title) {
+    const index = data.value.findIndex(_item => _item.id === item.id)
+    const newItemId = nanoid()
+    data.value.splice(index + 1, 0, { id: newItemId, content: '', lv: 4 })
+
+    nextTick(() => {
+      handleFocus(newItemId)
+    })
+  }
+}
+
+const handleBackspace = (e: Event, item: OutlineItem) => {
+  if (!item.title) {
+    const value = (e.target as HTMLInputElement).value
+    if (!value) deleteItem(item.id)
+  }
+}
+
+const addItem = (itemId: string, pos: 'next' | 'prev', content: string) => {
+  const index = data.value.findIndex(_item => _item.id === itemId)
+  const item = data.value[index]
+  if (!item) return
+
+  const id = nanoid()
+  let lv = 4
+  let i = 0
+  let title = false
+
+  if (pos === 'prev') i = index
+  else i = index + 1
+
+  if (item.lv === 1) lv = 2
+  else if (item.lv === 2) {
+    if (pos === 'prev') lv = 2
+    else lv = 3
+  }
+  else if (item.lv === 3) {
+    if (pos === 'prev') lv = 3
+    else lv = 4
+  }
+  else lv = 4
+
+  if (lv < 4) title = true
+
+  data.value.splice(i, 0, { id, content, lv, title })
+}
+
+const deleteItem = (itemId: string, isTitle?: boolean) => {
+  if (isTitle) {
+    const index = data.value.findIndex(item => item.id === itemId)
+
+    const targetIds = [itemId]
+    const item = data.value[index]
+    for (let i = index + 1; i < data.value.length; i++) {
+      const afterItem = data.value[i]
+      if (afterItem && afterItem.lv > item.lv) {
+        targetIds.push(afterItem.id)
+      }
+      else break
+    }
+    data.value = data.value.filter(item => !targetIds.includes(item.id))
+  }
+  else {
+    data.value = data.value.filter(item => item.id !== itemId)
+  }
+}
+
+const contextmenus = (el: HTMLElement): ContextmenuItem[] => {
+  const lv = +el.dataset.lv!
+  const id = el.dataset.id!
+
+  if (lv === 1) {
+    return [
+      {
+        text: '添加子级大纲(章)',
+        handler: () => addItem(id, 'next', '新的一章'),
+      },
+    ]
+  }
+  else if (lv === 2) {
+    return [
+      {
+        text: '上方添加同级大纲(章)',
+        handler: () => addItem(id, 'prev', '新的一章'),
+      },
+      {
+        text: '添加子级大纲(节)',
+        handler: () => addItem(id, 'next', '新的一节'),
+      },
+      {
+        text: '删除此章',
+        handler: () => deleteItem(id, true),
+      },
+    ]
+  }
+  else if (lv === 3) {
+    return [
+      {
+        text: '上方添加同级大纲(节)',
+        handler: () => addItem(id, 'prev', '新的一节'),
+      },
+      {
+        text: '添加子级大纲(项)',
+        handler: () => addItem(id, 'next', '新的一项'),
+      },
+      {
+        text: '删除此节',
+        handler: () => deleteItem(id, true),
+      },
+    ]
+  }
+  return [
+    {
+      text: '上方添加同级大纲(项)',
+      handler: () => addItem(id, 'prev', '新的一项'),
+    },
+    {
+      text: '下方添加同级大纲(项)',
+      handler: () => addItem(id, 'next', '新的一项'),
+    },
+    {
+      text: '删除此项',
+      handler: () => deleteItem(id),
+    },
+  ]
+}
+</script>
+
+<style lang="scss">
+.outline-editor {
+  padding: 0 10px;
+  padding-left: 40px;
+  position: relative;
+
+  .item {
+    height: 32px;
+    position: relative;
+
+    &.contextmenu-active {
+      color: $themeColor;
+
+      .text {
+        background-color: rgba($color: $themeColor, $alpha: .08);
+      }
+    }
+
+    &.title {
+      font-weight: 700;
+    }
+    &.lv-1 {
+      font-size: 22px;
+    }
+    &.lv-2 {
+      font-size: 17px;
+    }
+    &.lv-3 {
+      font-size: 15px;
+    }
+    &.lv-4 {
+      font-size: 13px;
+      padding-left: 20px;
+    }
+  }
+  .text {
+    height: 100%;
+    padding: 0 11px;
+    line-height: 32px;
+    border-radius: $borderRadius;
+    transition: background-color .2s;
+    cursor: pointer;
+    @include ellipsis-oneline();
+
+    &:hover {
+      background-color: rgba($color: $themeColor, $alpha: .08);
+    }
+  }
+  .flag {
+    width: 32px;
+    height: 32px;
+    position: absolute;
+    top: 50%;
+    left: -40px;
+    margin-top: -16px;
+    z-index: 1;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &::before {
+      content: '';
+      width: 1px;
+      height: 100%;
+      position: absolute;
+      left: 50%;
+      background-color: rgba($color: $themeColor, $alpha: .1);
+    }
+    &::after {
+      content: '';
+      width: 32px;
+      height: 22px;
+      border-radius: 2px;
+      background-color: #fff;
+      border: 1px solid $themeColor;
+      color: $themeColor;
+      position: relative;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 12px;
+      font-weight: 400;
+    }
+  }
+  .item.lv-1 .flag::after {
+    content: '主题';
+  }
+  .item.lv-2 .flag::after {
+    content: '章';
+  }
+  .item.lv-3 .flag::after {
+    content: '节';
+  }
+  .item.lv-4 .flag::after {
+    opacity: 0;
+  }
+}
+</style>

+ 111 - 0
src/components/Popover.vue

@@ -0,0 +1,111 @@
+<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, useTemplateRef } 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
+  (event: 'show'): void
+  (event: 'hide'): void
+}>()
+
+const instance = ref<Instance>()
+const contentVisible = ref(false)
+const triggerRef = useTemplateRef<HTMLElement>('triggerRef')
+const contentRef = useTemplateRef<HTMLElement>('contentRef')
+
+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)
+        emit('show')
+      }
+    },
+    onHidden() {
+      if (props.value) {
+        emit('update:value', false)
+        emit('hide')
+      }
+      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, useTemplateRef } 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 width = ref(0)
+const searchKey = ref('')
+const selectRef = useTemplateRef<HTMLElement>('selectRef')
+const searchInputRef = useTemplateRef<InstanceType<typeof Input>>('searchInputRef')
+
+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>

+ 120 - 0
src/components/SelectCustom.vue

@@ -0,0 +1,120 @@
+<template>
+  <div class="select-wrap" v-if="disabled">
+    <div class="select disabled" ref="selectRef">
+      <div class="selector"><slot name="label"></slot></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>
+      <div class="options" :style="{ width: width + 2 + 'px' }" @click="popoverVisible = false">
+        <slot name="options"></slot>
+      </div>
+    </template>
+    <div class="select" ref="selectRef">
+      <div class="selector"><slot name="label"></slot></div>
+      <div class="icon">
+        <slot name="icon">
+          <IconDown :size="14" />
+        </slot>
+      </div>
+    </div>
+  </Popover>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
+import Popover from './Popover.vue'
+
+withDefaults(defineProps<{
+  disabled?: boolean
+}>(), {
+  disabled: false,
+})
+
+const popoverVisible = ref(false)
+const selectRef = useTemplateRef<HTMLElement>('selectRef')
+const width = ref(0)
+
+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)
+})
+</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;
+}
+.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, useTemplateRef } 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 = useTemplateRef<HTMLElement>('sliderRef')
+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>

+ 94 - 0
src/components/TextArea.vue

@@ -0,0 +1,94 @@
+<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)"
+    @keydown.enter="$event => emit('enter', $event)"
+  ></textarea>
+</template>
+
+<script lang="ts" setup>
+import { useTemplateRef } 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
+  (event: 'enter', payload: KeyboardEvent): void
+}>()
+
+const handleInput = (e: Event) => {
+  emit('update:value', (e.target as HTMLInputElement).value)
+}
+
+const textareaRef = useTemplateRef<HTMLTextAreaElement>('textareaRef')
+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>

+ 474 - 0
src/components/WritingBoard.vue

@@ -0,0 +1,474 @@
+<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" />
+      </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" />
+      </div>
+      <div 
+        class="pen"
+        :style="{
+          left: mouse.x - 20 + 'px',
+          top: mouse.y - 20 + 'px',
+          color: color,
+        }"
+        v-if="model === 'shape'"
+      >
+        <IconPlus class="icon" :size="40" />
+      </div>
+    </template>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, onUnmounted, ref, watch, useTemplateRef } from 'vue'
+
+const props = withDefaults(defineProps<{
+  color?: string
+  model?: 'pen' | 'eraser' | 'mark' | 'shape'
+  shapeType?: 'rect' | 'circle' | 'arrow'
+  blackboard?: boolean
+  penSize?: number
+  markSize?: number
+  rubberSize?: number
+  shapeSize?: number
+}>(), {
+  color: '#ffcc00',
+  model: 'pen',
+  shapeType: 'rect',
+  blackboard: false,
+  penSize: 6,
+  markSize: 24,
+  rubberSize: 80,
+  shapeSize: 4,
+})
+
+const emit = defineEmits<{
+  (event: 'end'): void
+}>()
+
+let ctx: CanvasRenderingContext2D | null = null
+const writingBoardRef = useTemplateRef<HTMLElement>('writingBoardRef')
+const canvasRef = useTemplateRef<HTMLCanvasElement>('canvasRef')
+
+let lastPos = {
+  x: 0,
+  y: 0,
+}
+let isMouseDown = false
+let lastTime = 0
+let lastLineWidth = -1
+
+let initialImageData: ImageData | null = null
+
+// 鼠标位置坐标:用于画笔或橡皮位置跟随
+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' || props.model === 'shape') {
+    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 drawShape = (currentX: number, currentY: number) => {
+  if (!ctx || !initialImageData) return
+
+  ctx.putImageData(initialImageData, 0, 0)
+
+  const startX = lastPos.x
+  const startY = lastPos.y
+
+  ctx.save()
+  ctx.lineCap = 'butt'
+  ctx.lineJoin = 'miter'
+
+  ctx.beginPath()
+  if (props.shapeType === 'rect') {
+    const width = currentX - startX
+    const height = currentY - startY
+    ctx.rect(startX, startY, width, height)
+  } 
+  else if (props.shapeType === 'circle') {
+    const width = currentX - startX
+    const height = currentY - startY
+    const centerX = startX + width / 2
+    const centerY = startY + height / 2
+    const radiusX = Math.abs(width) / 2
+    const radiusY = Math.abs(height) / 2
+
+    ctx.ellipse(
+      centerX,
+      centerY,
+      Math.abs(radiusX),
+      Math.abs(radiusY),
+      0,
+      0,
+      Math.PI * 2,
+    )
+  }
+  else if (props.shapeType === 'arrow') {
+    const dx = currentX - startX
+    const dy = currentY - startY
+    const angle = Math.atan2(dy, dx)
+    const arrowLength = Math.max(props.shapeSize, 4) * 2
+    
+    const endX = currentX - (Math.cos(angle) * arrowLength)
+    const endY = currentY - (Math.sin(angle) * arrowLength)
+
+    ctx.moveTo(startX, startY)
+    ctx.lineTo(endX, endY)
+  }
+
+  ctx.strokeStyle = props.color
+  ctx.lineWidth = props.shapeSize
+  ctx.stroke()
+  ctx.restore()
+
+  if (props.shapeType === 'arrow') {
+    const dx = currentX - startX
+    const dy = currentY - startY
+    const angle = Math.atan2(dy, dx)
+    
+    const arrowLength = Math.max(props.shapeSize, 4) * 2.6
+    const arrowWidth = Math.max(props.shapeSize, 4) * 1.6
+    
+    const arrowBaseX = currentX - (Math.cos(angle) * arrowLength)
+    const arrowBaseY = currentY - (Math.sin(angle) * arrowLength)
+
+    ctx.save()
+    ctx.beginPath()
+    
+    ctx.moveTo(currentX, currentY)
+    
+    const leftX = arrowBaseX + arrowWidth * Math.cos(angle + Math.PI / 2)
+    const leftY = arrowBaseY + arrowWidth * Math.sin(angle + Math.PI / 2)
+    const rightX = arrowBaseX + arrowWidth * Math.cos(angle - Math.PI / 2)
+    const rightY = arrowBaseY + arrowWidth * Math.sin(angle - Math.PI / 2)
+
+    ctx.lineTo(leftX, leftY)
+    ctx.lineTo(rightX, rightY)
+    ctx.closePath()
+
+    ctx.fillStyle = props.color
+    ctx.fill()
+    ctx.restore()
+  }
+}
+
+// 路径操作
+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
+
+    lastPos = { x, y }
+    lastTime = new Date().getTime()
+  }
+  else if (props.model === 'mark') {
+    draw(x, y, props.markSize)
+    lastPos = { x, y }
+  }
+  else if (props.model ==='eraser') {
+    erase(x, y)
+    lastPos = { x, y }
+  }
+  else if (props.model === 'shape') {
+    drawShape(x, y)
+  }
+}
+
+// 获取鼠标在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
+
+  if (props.model === 'shape') {
+    initialImageData = ctx!.getImageData(0, 0, canvasRef.value!.width, canvasRef.value!.height)
+  }
+  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: 40,
+  image: 20,
+  shape: 20,
+  chart: 200,
+  table: 30,
+  video: 250,
+  audio: 20,
+  latex: 20,
+}

+ 25 - 0
src/configs/font.ts

@@ -0,0 +1,25 @@
+export const FONTS = [
+  { label: '默认字体', value: '' },
+  { label: '思源黑体', value: 'SourceHanSans' },
+  { label: '思源宋体', value: 'SourceHanSerif' },
+  { label: '方正黑体', value: 'FangZhengHeiTi' },
+  { label: '方正楷体', value: 'FangZhengKaiTi' },
+  { label: '方正宋体', value: 'FangZhengShuSong' },
+  { label: '方正仿宋', value: 'FangZhengFangSong' },
+  { label: '阿里巴巴普惠体', value: 'AlibabaPuHuiTi' },
+  { label: '朱雀仿宋', value: 'ZhuqueFangSong' },
+  { label: '霞鹜文楷', value: 'LXGWWenKai' },
+  { label: '文鼎PL楷体', value: 'WenDingPLKaiTi' },
+  { label: '得意黑', value: 'DeYiHei' },
+  { label: 'MiSans', value: 'MiSans' },
+  { label: '仓耳小丸子', value: 'CangerXiaowanzi' },
+  { label: '优设标题黑', value: 'YousheTitleBlack' },
+  { label: '峰广明锐体', value: 'FengguangMingrui' },
+  { label: '摄图摩登小方体', value: 'ShetuModernSquare' },
+  { label: '站酷快乐体', value: 'ZcoolHappy' },
+  { label: '字制区喜脉体', value: 'ZizhiQuXiMai' },
+  { label: '素材集市康康体', value: 'SucaiJishiKangkang' },
+  { label: '素材集市酷方体', value: 'SucaiJishiCoolSquare' },
+  { label: '途牛类圆体', value: 'TuniuRounded' },
+  { label: '锐字真言体', value: 'RuiziZhenyan' },
+]

+ 149 - 0
src/configs/hotkey.ts

@@ -0,0 +1,149 @@
+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',
+}
+
+interface HotkeyItem {
+  type: string
+  children: {
+    label: string
+    value?: string
+  }[] 
+}
+
+export const HOTKEY_DOC: HotkeyItem[] = [
+  {
+    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` },
+    ],
+  },
+  {
+    type: '其他快捷操作',
+    children: [
+      { label: '添加图片 - 粘贴来自系统剪贴板的图片' },
+      { label: '添加图片 - 将本地图片拖拽到画布中' },
+      { label: '添加图片 - 在画布中粘贴SVG代码' },
+      { label: '添加图片 - 粘贴来自 pexels 的图片链接' },
+      { label: '添加文本 - 粘贴来自系统剪贴板的文字' },
+      { label: '添加文本 - 将外部选中文字拖拽到画布中' },
+      { label: '文本编辑 - 支持 markdown 语法创建列表和引用' },
+    ],
+  },
+]

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio