├── src
├── vite-env.d.ts
├── index.ts
├── composables
│ ├── transformation.ts
│ └── marquee.ts
├── types.ts
├── plotter.ts
├── utils.ts
└── components
│ └── VueSurf.ts
├── playground
├── src
│ ├── vite-env.d.ts
│ ├── main.ts
│ ├── style.css
│ ├── assets
│ │ └── github.svg
│ └── App.vue
├── vite.config.ts
├── tsconfig.node.json
├── package.json
├── tsconfig.json
├── index.html
└── package-lock.json
├── graphs
├── apex.png
├── cover.png
├── petal.png
├── repeat.png
├── closure.png
├── marquee.gif
├── ogImage.png
├── serrated.png
├── side-top.png
├── side-bottom.png
├── smooth-true.png
├── apexes-series.gif
├── smooth-false.png
├── linear-gradients.png
└── logo.svg
├── .vscode
└── extensions.json
├── tsconfig.node.json
├── .gitignore
├── .eslintrc.cjs
├── tsconfig.json
├── LICENSE
├── .github
└── workflows
│ └── main.yml
├── package.json
└── README.md
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/playground/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/graphs/apex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/apex.png
--------------------------------------------------------------------------------
/graphs/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/cover.png
--------------------------------------------------------------------------------
/graphs/petal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/petal.png
--------------------------------------------------------------------------------
/graphs/repeat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/repeat.png
--------------------------------------------------------------------------------
/graphs/closure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/closure.png
--------------------------------------------------------------------------------
/graphs/marquee.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/marquee.gif
--------------------------------------------------------------------------------
/graphs/ogImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/ogImage.png
--------------------------------------------------------------------------------
/graphs/serrated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/serrated.png
--------------------------------------------------------------------------------
/graphs/side-top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/side-top.png
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./types";
2 | export { VueSurf } from "./components/VueSurf";
3 |
--------------------------------------------------------------------------------
/graphs/side-bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/side-bottom.png
--------------------------------------------------------------------------------
/graphs/smooth-true.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/smooth-true.png
--------------------------------------------------------------------------------
/graphs/apexes-series.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/apexes-series.gif
--------------------------------------------------------------------------------
/graphs/smooth-false.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/smooth-false.png
--------------------------------------------------------------------------------
/graphs/linear-gradients.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/f820602h/vue-surf/HEAD/graphs/linear-gradients.png
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/playground/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import "./style.css";
3 | import App from "./App.vue";
4 |
5 | createApp(App).mount("#app");
6 |
--------------------------------------------------------------------------------
/playground/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import vue from "@vitejs/plugin-vue";
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [vue()],
7 | base: "/vue-surf/",
8 | });
9 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["*/*.ts", "*/**/*.ts"],
10 | }
11 |
--------------------------------------------------------------------------------
/playground/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | require("@rushstack/eslint-patch/modern-module-resolution");
3 |
4 | module.exports = {
5 | root: true,
6 | extends: [
7 | "plugin:vue/vue3-recommended",
8 | "eslint:recommended",
9 | "@vue/eslint-config-typescript/recommended",
10 | "@vue/eslint-config-prettier",
11 | ],
12 | rules: {
13 | "@typescript-eslint/no-explicit-any": "off",
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "type": "module",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vue-tsc && vite build",
7 | "preview": "vite preview"
8 | },
9 | "dependencies": {
10 | "vue": "^3.3.4"
11 | },
12 | "devDependencies": {
13 | "@vitejs/plugin-vue": "^4.2.3",
14 | "typescript": "^5.0.2",
15 | "vite": "^4.4.5",
16 | "vue-tsc": "^1.8.5"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/graphs/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
12 |
16 |
--------------------------------------------------------------------------------
/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "preserve",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "node",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "noEmit": true,
14 | "declaration": true,
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true
21 | },
22 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "src/components/VueSurf.ts"],
23 | "exclude": [ "dist", "playground"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Vue Surf Demo
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/composables/transformation.ts:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 |
3 | export function useTransformation() {
4 | const reqFrame = ref(0);
5 | const stamp = ref(0);
6 |
7 | const fps = 60;
8 | const interval = 1000 / fps;
9 | let then = 0;
10 | function draw(timestamp: number) {
11 | requestAnimationFrame(draw);
12 |
13 | if (then === undefined) then = timestamp;
14 | const delta = timestamp - then;
15 |
16 | if (delta > interval) {
17 | then = timestamp - (delta % interval);
18 | stamp.value = timestamp;
19 | }
20 | }
21 |
22 | function resume(): void {
23 | reqFrame.value = requestAnimationFrame(draw);
24 | }
25 |
26 | function pause(): void {
27 | reqFrame.value = 0;
28 | }
29 |
30 | return {
31 | stamp,
32 | pause,
33 | resume,
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/playground/src/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | color-scheme: light dark;
7 | color: rgba(255, 255, 255, 0.87);
8 | background-color: #242424;
9 |
10 | font-synthesis: none;
11 | text-rendering: optimizeLegibility;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | -webkit-text-size-adjust: 100%;
15 | }
16 |
17 | a {
18 | font-weight: 500;
19 | color: #646cff;
20 | text-decoration: inherit;
21 | }
22 | a:hover {
23 | color: #535bf2;
24 | }
25 |
26 | body {
27 | margin: 0;
28 | min-width: 320px;
29 | min-height: 100vh;
30 | }
31 |
32 | #app {
33 | width: 100vw;
34 | min-height: 100vh;
35 | display: flex;
36 | flex-direction: column;
37 | justify-content: space-between;
38 | }
39 |
--------------------------------------------------------------------------------
/playground/src/assets/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 max.lee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | build:
10 | name: Build
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout repo
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup Node
18 | uses: actions/setup-node@v3
19 |
20 | - name: Install dependencies
21 | uses: bahmutov/npm-install@v1
22 |
23 | - name: Build project
24 | run: npm run play:build
25 |
26 | - name: Upload production-ready build files
27 | uses: actions/upload-artifact@v3
28 | with:
29 | name: production-files
30 | path: ./playground/dist
31 |
32 | deploy:
33 | name: Deploy
34 | needs: build
35 | runs-on: ubuntu-latest
36 | if: github.ref == 'refs/heads/master'
37 |
38 | steps:
39 | - name: Download artifact
40 | uses: actions/download-artifact@v3
41 | with:
42 | name: production-files
43 | path: ./playground/dist
44 |
45 | - name: Deploy to GitHub Pages
46 | uses: peaceiris/actions-gh-pages@v3
47 | with:
48 | github_token: ${{ secrets.GITHUB_TOKEN }}
49 | publish_dir: ./playground/dist
50 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | export type LinearGradientColor = {
2 | name: string;
3 | rotate?: number;
4 | steps: {
5 | offset: number;
6 | color: string;
7 | opacity?: number;
8 | }[];
9 | };
10 |
11 | export type WaveSide = "top" | "bottom";
12 | export type WaveShape = "wavy" | "serrated" | "petal";
13 |
14 | export type Apex =
15 | | [number | string, number | string]
16 | | {
17 | distance: number | string;
18 | height: number | string;
19 | };
20 |
21 | export type Apexes =
22 | | Apex[]
23 | | {
24 | apexes: Apex[];
25 | shape?: WaveShape;
26 | color?: string | LinearGradientColor;
27 | };
28 |
29 | export type ApexesChangedCallback = (
30 | currentApexes: Apex[],
31 | currentShape: WaveShape,
32 | currentColor: string | LinearGradientColor,
33 | ) => void;
34 |
35 | export type WaveProps = {
36 | width?: number | string;
37 | shape?: WaveShape;
38 | side?: WaveSide;
39 | apexesSeries: Apexes[];
40 | apexesIndex?: number;
41 | color?: string | LinearGradientColor;
42 | repeat?: boolean;
43 | closure?: boolean;
44 | smooth?: boolean | number;
45 | marquee?: boolean;
46 | marqueeSpeed?: number;
47 | transitionDuration?: number;
48 | apexesSeriesTransformAuto?: boolean;
49 | apexesSeriesTransformDuration?: number;
50 | onApexesChanged?: ApexesChangedCallback;
51 | };
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-surf",
3 | "description": "Very customized animated svg wave Vue component",
4 | "private": false,
5 | "version": "2.0.0",
6 | "author": "max.lee ",
7 | "license": "MIT",
8 | "homepage": "https://github.com/f820602h/vue-surf#readme",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/f820602h/vue-surf.git"
12 | },
13 | "keywords": [
14 | "vue",
15 | "animation",
16 | "wave",
17 | "svg"
18 | ],
19 | "main": "./dist/index.js",
20 | "module": "./dist/index.mjs",
21 | "types": "./dist/index.d.ts",
22 | "files": [
23 | "dist"
24 | ],
25 | "exports": {
26 | ".": {
27 | "types": "./dist/index.d.ts",
28 | "import": "./dist/index.mjs",
29 | "require": "./dist/index.js"
30 | }
31 | },
32 | "scripts": {
33 | "dev": "pkgroll --watch",
34 | "build": "pkgroll --minify",
35 | "play": "vite playground --open",
36 | "play:build": "vite build playground",
37 | "lint": "eslint .",
38 | "typecheck": "vue-tsc --noEmit"
39 | },
40 | "dependencies": {
41 | "@vueuse/core": "^10.4.1",
42 | "uuidv4": "^6.2.13",
43 | "vue": "^3.3.4"
44 | },
45 | "devDependencies": {
46 | "@rushstack/eslint-patch": "^1.1.0",
47 | "@types/node": "^20.6.1",
48 | "@vitejs/plugin-vue": "^4.2.3",
49 | "@vue/eslint-config-prettier": "^8.0.0",
50 | "@vue/eslint-config-typescript": "^11.0.3",
51 | "eslint": "^8.49.0",
52 | "eslint-plugin-vue": "^9.17.0",
53 | "pkgroll": "^1.11.0",
54 | "prettier": "^3.0.3",
55 | "sass": "^1.66.1",
56 | "typescript": "^5.0.2",
57 | "vite": "^4.4.5",
58 | "vue-tsc": "^1.8.5"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/composables/marquee.ts:
--------------------------------------------------------------------------------
1 | import { type Ref, ref } from "vue";
2 |
3 | export function useMarquee(elementRef: Ref, speed: number) {
4 | function standardize(number: number): number {
5 | return Math.min(Math.max(number, -25), 25);
6 | }
7 | const initSpeed = ref(standardize(speed));
8 | const currentSpeed = ref(standardize(speed));
9 | const reqFrame = ref(0);
10 | const offset = ref(0);
11 | const isStop = ref(false);
12 |
13 | const resumeTimer = ref(0);
14 | const pauseTimer = ref(0);
15 |
16 | const fps = 60;
17 | const interval = 1000 / fps;
18 | let then = 0;
19 | function draw(timestamp: number): void {
20 | requestAnimationFrame(draw);
21 |
22 | if (then === undefined) then = timestamp;
23 | const delta = timestamp - then;
24 |
25 | if (delta > interval) {
26 | then = timestamp - (delta % interval);
27 |
28 | if (isStop.value) return;
29 | if (!elementRef.value) return;
30 | if (!(elementRef.value.children[0] instanceof HTMLElement)) return;
31 |
32 | const element = elementRef.value.children[0];
33 | element.style.transform = `translateX(${offset.value}px)`;
34 | if (Math.abs(offset.value) > element.clientWidth / 5) offset.value = 0;
35 | offset.value = offset.value + currentSpeed.value;
36 | }
37 | }
38 |
39 | function start(): void {
40 | window.clearInterval(resumeTimer.value);
41 | window.clearInterval(pauseTimer.value);
42 | isStop.value = false;
43 | offset.value = 0;
44 | currentSpeed.value = initSpeed.value;
45 | reqFrame.value = requestAnimationFrame(draw);
46 | }
47 |
48 | function stop(): void {
49 | window.clearInterval(resumeTimer.value);
50 | window.clearInterval(pauseTimer.value);
51 | window.cancelAnimationFrame(reqFrame.value);
52 | isStop.value = true;
53 | currentSpeed.value = 0;
54 | reqFrame.value = 0;
55 | }
56 |
57 | function resetSpeed(speed: number) {
58 | initSpeed.value = standardize(speed);
59 | currentSpeed.value = standardize(speed);
60 | }
61 |
62 | return {
63 | start,
64 | resetSpeed,
65 | stop,
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/src/plotter.ts:
--------------------------------------------------------------------------------
1 | import type { WaveSide, WaveShape } from "./types";
2 | import { getLengthPercentNumber, average } from "./utils";
3 |
4 | export function plotter({
5 | apexesPixelSet,
6 | totalDistance,
7 | waveHeight,
8 | side,
9 | shape,
10 | smooth,
11 | }: {
12 | apexesPixelSet: number[][];
13 | totalDistance: number;
14 | waveHeight: number;
15 | side: WaveSide;
16 | shape: WaveShape;
17 | smooth: boolean | number;
18 | }): string {
19 | function getHeightPercent(h: number) {
20 | const height = side === "bottom" ? h : waveHeight - h;
21 | return getLengthPercentNumber(height, waveHeight);
22 | }
23 |
24 | function getDistancePercent(d: number) {
25 | return getLengthPercentNumber(d, totalDistance);
26 | }
27 |
28 | const origin = side === "bottom" ? "-0.1" : "100.1";
29 | let path = "";
30 | let sumDistance = 0;
31 |
32 | path += apexesPixelSet.reduce((acc, [d, h], index, arr) => {
33 | const halfBetweenPrev = average(d, 0);
34 | const apexSvgXPercent = getDistancePercent(sumDistance + d);
35 | const apexSvgYPercent = getHeightPercent(h);
36 |
37 | if (index === 0) return (acc += `${apexSvgYPercent}`);
38 |
39 | const nextIndex = index !== arr.length - 1 ? index + 1 : 1;
40 | const halfBetweenNext = average(arr[nextIndex][0], 0);
41 | const prevApexSvgXPercent = getDistancePercent(sumDistance);
42 | const prevApexSvgYPercent = getHeightPercent(arr[index - 1][1]);
43 | const middleBetweenPrevSvgX = sumDistance + halfBetweenPrev;
44 |
45 | const smoothness =
46 | halfBetweenPrev < halfBetweenNext && smooth
47 | ? ((halfBetweenPrev + halfBetweenNext) / halfBetweenNext) *
48 | halfBetweenPrev *
49 | Math.max(0, Math.min(1, Number(smooth)))
50 | : 0;
51 |
52 | let firstControlPointX: number;
53 | let firstControlPointY: number;
54 | let secondControlPointX: number;
55 | let secondControlPointY: number;
56 |
57 | switch (shape) {
58 | case "wavy":
59 | {
60 | firstControlPointX = getDistancePercent(
61 | middleBetweenPrevSvgX + smoothness,
62 | );
63 | firstControlPointY = prevApexSvgYPercent;
64 |
65 | secondControlPointX = getDistancePercent(
66 | middleBetweenPrevSvgX - smoothness,
67 | );
68 | secondControlPointY = apexSvgYPercent;
69 | }
70 | break;
71 |
72 | case "serrated":
73 | {
74 | firstControlPointX = prevApexSvgXPercent;
75 | firstControlPointY = prevApexSvgYPercent;
76 |
77 | secondControlPointX = apexSvgXPercent;
78 | secondControlPointY = apexSvgYPercent;
79 | }
80 | break;
81 |
82 | case "petal":
83 | {
84 | firstControlPointX =
85 | h < arr[index - 1][1] ? apexSvgXPercent : prevApexSvgXPercent;
86 | firstControlPointY =
87 | h < arr[index - 1][1] ? prevApexSvgYPercent : apexSvgYPercent;
88 |
89 | secondControlPointX = apexSvgXPercent;
90 | secondControlPointY = apexSvgYPercent;
91 | }
92 | break;
93 | default: {
94 | firstControlPointX = getDistancePercent(
95 | middleBetweenPrevSvgX + smoothness,
96 | );
97 | firstControlPointY = prevApexSvgYPercent;
98 |
99 | secondControlPointX = getDistancePercent(
100 | middleBetweenPrevSvgX - smoothness,
101 | );
102 | secondControlPointY = apexSvgYPercent;
103 | }
104 | }
105 |
106 | const firstControlPoint = `${firstControlPointX} ${firstControlPointY}`;
107 | const secondControlPoint = `${secondControlPointX} ${secondControlPointY}`;
108 | const end = `${apexSvgXPercent} ${apexSvgYPercent}`;
109 |
110 | sumDistance += d;
111 |
112 | return (acc += ` C${firstControlPoint}, ${secondControlPoint}, ${end}`);
113 | }, "");
114 |
115 | return `M0 ${origin} L0 ${path} L${getDistancePercent(
116 | sumDistance,
117 | )} ${origin}Z`;
118 | }
119 |
--------------------------------------------------------------------------------
/playground/src/App.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
43 |
44 |
45 |
46 |
47 |
48 |
Vue Surf
49 |
50 |
Let's go surfing together!
51 |
$ npm i vue-surf
52 |
→ Home Page
53 |
54 |
55 |
56 |
82 |
108 |
130 |
131 |
132 |
133 |
134 |
192 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Apex,
3 | Apexes,
4 | WaveShape,
5 | WaveSide,
6 | LinearGradientColor,
7 | } from "./types";
8 |
9 | const pixelRegex = /(\d+)px/;
10 | const percentRegex = /(\d+)%/;
11 |
12 | export const errorText = {
13 | parentNoWidth:
14 | "[Vue Surf] Parent element must have a width when using '%' to set width.",
15 | lengthPositive: "[Vue Surf] Please provide a positive length value.",
16 | lengthFormat:
17 | "[Vue Surf] Length must be a positive number or string with unit 'px' or '%'.",
18 |
19 | apexFormat:
20 | "[Vue Surf] Apex must be an array of [number | string, number | string] or { distance: number, height: number }.",
21 |
22 | apexesFormat: "[Vue Surf] Apexes need at least two apexes.",
23 | apexesHeightZero:
24 | "[Vue Surf] At least one apex height must be greater than 0.",
25 | apexesLengthZero:
26 | "[Vue Surf] At least one apex length must be greater than 0.",
27 |
28 | apexesSeriesFormat: "[Vue Surf] ApexesSeries must be a not empty array.",
29 | apexesLengthChanged:
30 | "[Vue Surf] The length of apexes changed, animation may be broken.",
31 |
32 | apexesSeriesLengthOnlyOne:
33 | "[Vue Surf] The length of 'apexesSeries' must be greater than 1 to initiate the transformation animation",
34 |
35 | apexesTotalDistanceChanged:
36 | "[Vue Surf] Apexes total distance changed, animation may be broken.",
37 |
38 | colorFormat: "[Vue Surf] Color must be a string or object.",
39 | colorNameFormat: "[Vue Surf] Name in color must be a string.",
40 | colorRotateFormat: "[Vue Surf] Rotate in color must be a number.",
41 | colorStepOpacityFormat: "[Vue Surf] Opacity in step must be a number.",
42 | colorStepsFormat:
43 | "[Vue Surf] Steps in color must be a not empty array of { offset: number, color: string, opacity?: number }.",
44 |
45 | shape: "[Vue Surf] Shape must be one of 'wavy', 'serrated', 'petal'.",
46 | side: "[Vue Surf] Side must be one of 'top', 'bottom'.",
47 | };
48 |
49 | export function lengthValidator(val: number | string): boolean {
50 | if (typeof val === "number" && val < 0) {
51 | throw new Error(errorText.lengthPositive);
52 | }
53 | if (typeof val === "string") {
54 | if (!pixelRegex.test(val) && !percentRegex.test(val)) {
55 | throw new Error(errorText.lengthFormat);
56 | } else if (parseInt(val) <= 0) {
57 | throw new Error(errorText.lengthPositive);
58 | }
59 | }
60 | return true;
61 | }
62 |
63 | export function apexesValidator(val: Apex[]): boolean {
64 | if (!val) return true;
65 | if (!Array.isArray(val) || (Array.isArray(val) && val.length < 1)) {
66 | throw new Error(errorText.apexesFormat);
67 | }
68 |
69 | const isValidFormat = val.every((apex) => {
70 | if (Array.isArray(apex) && apex.length === 2) {
71 | return lengthValidator(apex[0]) && lengthValidator(apex[1]);
72 | } else if ("distance" in apex && "height" in apex) {
73 | return lengthValidator(apex.distance) && lengthValidator(apex.height);
74 | } else {
75 | throw new Error(errorText.apexesLengthZero);
76 | }
77 | });
78 |
79 | const atLeastOneDistanceNotZero = val.some((apex) => {
80 | if (Array.isArray(apex) && apex.length === 2) {
81 | return parseInt(apex[0] + "") > 0;
82 | } else if ("distance" in apex) {
83 | return parseInt(apex.distance + "") > 0;
84 | } else {
85 | throw new Error(errorText.apexesHeightZero);
86 | }
87 | });
88 |
89 | const atLeastOneHeightNotZero = val.some((apex) => {
90 | if (Array.isArray(apex) && apex.length === 2) {
91 | return parseInt(apex[1] + "") > 0;
92 | } else if ("height" in apex) {
93 | return parseInt(apex.height + "") > 0;
94 | } else {
95 | throw new Error(errorText.apexFormat);
96 | }
97 | });
98 |
99 | return isValidFormat && atLeastOneDistanceNotZero && atLeastOneHeightNotZero;
100 | }
101 |
102 | export function apexesSeriesValidator(val: Apexes[]): boolean {
103 | if (!val) return true;
104 | if (!Array.isArray(val) || (Array.isArray(val) && val.length === 0)) {
105 | throw new Error(errorText.apexesSeriesFormat);
106 | }
107 | return val.every((apexes) => {
108 | if (Array.isArray(apexes)) {
109 | return apexesValidator(apexes);
110 | } else if ("apexes" in apexes) {
111 | if ("shape" in apexes && !!apexes.shape) {
112 | return shapeValidator(apexes.shape);
113 | }
114 | return apexesValidator(apexes.apexes);
115 | }
116 | return false;
117 | });
118 | }
119 |
120 | export function shapeValidator(val: string): val is WaveShape {
121 | const waveShape: string[] = ["wavy", "serrated", "petal"];
122 | if (!val) return true;
123 | return waveShape.indexOf(val) !== -1;
124 | }
125 |
126 | export function sideValidator(val: string): val is WaveSide {
127 | if (!val) return true;
128 | const waveSide: string[] = ["top", "bottom"];
129 | return waveSide.indexOf(val) !== -1;
130 | }
131 |
132 | export function colorValidator(val: string | LinearGradientColor): boolean {
133 | if (!val) return true;
134 | if (typeof val === "string") return true;
135 | if (typeof val === "object") {
136 | if ("rotate" in val && typeof val.rotate !== "number") {
137 | throw new Error(errorText.colorRotateFormat);
138 | }
139 | if (!("name" in val) || typeof val.name !== "string") {
140 | throw new Error(errorText.colorNameFormat);
141 | }
142 | if (
143 | !Array.isArray(val.steps) ||
144 | (Array.isArray(val.steps) && val.steps.length === 0)
145 | ) {
146 | throw new Error(errorText.colorStepsFormat);
147 | }
148 |
149 | return val.steps.every((colorStep) => {
150 | if ("opacity" in colorStep && typeof colorStep.opacity !== "number") {
151 | throw new Error(errorText.colorStepOpacityFormat);
152 | }
153 | if (!("offset" in colorStep) || !("color" in colorStep)) {
154 | throw new Error(errorText.colorStepsFormat);
155 | }
156 | return (
157 | typeof colorStep.offset === "number" &&
158 | typeof colorStep.color === "string"
159 | );
160 | });
161 | }
162 | throw new Error(errorText.colorFormat);
163 | }
164 |
165 | export function getLengthPixelNumber(
166 | length: number | string,
167 | total: number,
168 | ): number {
169 | if (typeof length === "number") {
170 | return length;
171 | } else if (typeof length === "string") {
172 | if (pixelRegex.test(length)) {
173 | const match = pixelRegex.exec(length);
174 | if (match) return Number(match[1]);
175 | } else if (percentRegex.test(length)) {
176 | const match = percentRegex.exec(length);
177 | if (match) return Math.round((Number(match[1]) * total) / 100);
178 | }
179 | }
180 | return 0;
181 | }
182 |
183 | export function getLengthPercentNumber(
184 | length: number | string,
185 | total: number,
186 | ): number {
187 | if (typeof length === "number") {
188 | return Number(((length / total) * 100).toFixed(2));
189 | } else if (typeof length === "string") {
190 | if (pixelRegex.test(length)) {
191 | const match = pixelRegex.exec(length);
192 | if (match) return Number(((Number(match[1]) / total) * 100).toFixed(2));
193 | } else if (percentRegex.test(length)) {
194 | const match = percentRegex.exec(length);
195 | if (match) return Number(match[1]);
196 | }
197 | }
198 | return 0;
199 | }
200 |
201 | export function average(...arg: number[]): number {
202 | return arg.reduce((a, b) => a + b, 0) / arg.length;
203 | }
204 |
205 | export function debounce(func: () => void) {
206 | let timer = 0;
207 | return function () {
208 | if (timer) clearTimeout(timer);
209 | timer = window.setTimeout(func, 300);
210 | };
211 | }
212 |
213 | // function findLCM(a: number, b: number): number {
214 | // function findGCD(x: number, y: number): number {
215 | // return y === 0 ? x : findGCD(y, x % y);
216 | // }
217 | // return (a * b) / findGCD(a, b);
218 | // }
219 |
220 | // export function findArrayLCM(arr: number[]): number {
221 | // let lcm = arr[0];
222 | // for (let i = 1; i < arr.length; i++) lcm = findLCM(lcm, arr[i]);
223 | // return lcm;
224 | // }
225 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vue-Surf
8 |
9 |
10 | Very customized animated svg wave Vue component
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Demo
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | ## Why ?
31 | Current solutions for adding cool waves to websites are often less controllable. Many SVG Wave Generators generate random shapes, if you want to add animation, the randomness factor increases even more.
32 |
33 | And the output is usually just image files. If you want to make changes later on, you'll need to regenerate them.
34 |
35 |
36 |
37 | ## Install
38 |
39 | ```
40 | npm install vue-surf
41 | ```
42 |
43 | ## Usage
44 |
45 | Add `` component from `vue-surf` to your template, and Pass in at least two parameters, `width` and `apexesSeries` to ``.
46 |
47 | ```html
48 |
51 |
52 |
53 |
63 |
64 | ```
65 | And Ta-da!
66 |
67 |
68 |
69 | ## Props
70 |
71 | ### width
72 | ```typescript
73 | width: {
74 | type: [Number | String],
75 | default: "100%"
76 | }
77 | ```
78 |
79 | The width of the wave, accept direct `number` values representing pixels or `string` with `px` or `%` units."
80 |
81 |
82 |
83 | ### color
84 | ```typescript
85 | type LinearGradientColor = {
86 | name: string;
87 | rotate?: number;
88 | steps: {
89 | offset: number;
90 | color: string;
91 | opacity?: number
92 | }[];
93 | }
94 |
95 | color: {
96 | type: [String, Object] as PropType,
97 | default: "white",
98 | }
99 | ```
100 | Configuring the fill color of the wave, it accept standard monochrome `string` or utilize a specific object format to configure linear gradients.
101 |
102 | ```typescript
103 | const color = reactive({
104 | name: "myGradient" // name must be specified
105 | rotate: 90,
106 | steps: [
107 | { offset: 0, color: '#FEAC5E', opacity: 0.3 },
108 | { offset: 0.5, color: '#C779D0' },
109 | { offset: 1, color: '#4BC0C8' },
110 | ],
111 | })
112 | ```
113 |
114 |
115 |
116 | > Radial gradients are currently not supported
117 |
118 |
119 |
120 | ### shape
121 | ```typescript
122 | type WaveShape = "wavy" | "serrated" | "petal";
123 |
124 | shape: {
125 | type: String as () => WaveShape,
126 | default: "wavy",
127 | }
128 | ```
129 | In addition to the regular wave pattern, VueSurf also offer options for a serrated and a petal-like pattern for you to choose from.
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | ### apexesSeries
139 | ```typescript
140 | type Apex =
141 | | [number | string, number | string]
142 | | {
143 | distance: number | string;
144 | height: number | string;
145 | };
146 |
147 | type Apexes =
148 | | Apex[]
149 | | {
150 | apexes: Apex[];
151 | shape?: WaveShape;
152 | color?: string | LinearGradientColor;
153 | };
154 |
155 | apexesSeries: {
156 | type: Array as () => Apexes[],
157 | default: undefined,
158 | }
159 | ```
160 | #### What is Apex?
161 |
162 | An apex is configured as the smallest unit of a wave, consisting of both `distance` and `height`. You can represent it using either an object or an array.
163 |
164 | ```typescript
165 | const apex = ref([0, "50%"]); // [distance, height]
166 | // or
167 | const apex = ref({ distance: 0, height: "50%" });
168 | ```
169 |
170 | Here, the term `distance` refers to the length of separation from the **previous apex**. And `height` denotes the vertical distance of the apex from the reference plane used for calculation.
171 |
172 | Both `distance` and `height` accept direct `number` values representing pixels or `string` with `px` or `%` units.
173 |
174 |
175 |
176 | > ⚠️ It is noteworthy that when configured using percentages, they will be calculated relative to the width of the wave.
177 |
178 | #### What is Apexes?
179 |
180 | Apexes are an array composed of multiple `apex`, and you can freely use either object or array formats for each apex within the array.
181 |
182 | ```typescript
183 | const apexes = ref([
184 | { distance: 0, height: "50%" },
185 | [100, 50],
186 | ["100px", 120]
187 | ]);
188 | ```
189 |
190 | Additionally, if you wish to set apexes with shapes or colors distinct from the `shape` and `color` props, you can describe apexes using objects and specify the 'shape' and 'color' properties.
191 |
192 | ```typescript
193 | const myApexes = ref({
194 | apexes: [
195 | { distance: 0, height: "20%" },
196 | [100, 50],
197 | ["100px", 120]
198 | ],
199 | shape: "petal",
200 | color: "lightblue"
201 | });
202 | ```
203 |
204 | > ⚠️ Due to the absence of a preceding apex for the first apex, any distance configuration for it will be disregarded.
205 |
206 | #### Setting ApexesSeries
207 |
208 | ApexesSeries is an array composed of one or multiple `apexes`. Similarly, you have the option to utilize either object or array formats for apexes within it."
209 |
210 | ```typescript
211 | const apexesSeries = ref([
212 | [
213 | { distance: 0, height: "20%" },
214 | [120, 0],
215 | [120, 120]
216 | ],
217 | {
218 | apexes: [
219 | { distance: 0, height: "10%" },
220 | [120, 0],
221 | [120, 60]
222 | ],
223 | shape: "petal",
224 | color: "lightblue"
225 | }
226 | ])
227 | ```
228 |
229 | When you pass an ApexesSeries with a length greater than 1, the wave transformation animation will automatically activate. However, you can disable it by setting `apexesSeriesTransformAuto` to `false`.
230 |
231 |
232 |
233 |
234 |
235 | Of course, you can also directly replace the values of ApexesSeries dynamically to achieve the transformation effect.
236 |
237 | ```typescript
238 | const apexesSeries = ref([
239 | [
240 | [0, 50],
241 | [100, 0],
242 | [100, 50]
243 | ],
244 | ])
245 |
246 | apexesSeries.value = [
247 | [0, 100],
248 | [50, 0],
249 | [50, 100]
250 | ]
251 | ```
252 |
253 | > ⚠️ Please ensure that each `apexes` within the `apexesSeries` possesses an equal **length** and **total distance** to maintain the effectiveness of the transformation animation."
254 |
255 |
256 |
257 | ### apexesIndex
258 | ```typescript
259 | apexesIndex: {
260 | type: Number,
261 | default: undefined,
262 | }
263 | ```
264 | You can specify `apexesIndex` to determine which set of `apexes` from `apexesSeries` the wave should display.When it is configured, the transformation animation will be deactivated.
265 |
266 |
267 |
268 |
269 | ### side
270 | ```typescript
271 | type WaveSide = "top" | "bottom";
272 |
273 | side: {
274 | type: String as () => WaveSide,
275 | default: "top",
276 | }
277 | ```
278 | The determination of whether the wave faces upwards or downwards also dictates whether the height of the apexes is measured from the top or the bottom.
279 |
280 | ```html
281 |
282 |
283 |
294 |
295 |
296 |
297 |
308 |
309 |
310 | ```
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 | ### repeat
320 | ```typescript
321 | repeat: {
322 | type: Boolean,
323 | default: true,
324 | }
325 | ```
326 | The decision to automatically repeat is contingent upon whether the cumulative `distance` set by an `apexes` is insufficient to cover the entire `width` of the wave.
327 |
328 | ```html
329 |
330 |
331 |
342 |
343 | ```
344 |
345 |
346 |
347 |
348 |
349 |
350 | ### closure
351 | ```typescript
352 | closure: {
353 | type: Boolean,
354 | default: true,
355 | }
356 | ```
357 | To facilitate a natural alignment of repeated waves, the height of the last apex in an `apexes` will automatically align with the height of the first apex. You can set it to `false` to disable this behavior.
358 |
359 | ```html
360 |
361 |
362 |
373 |
374 | ```
375 |
376 |
377 |
378 |
379 |
380 |
381 | ### smooth
382 | ```typescript
383 | smooth: {
384 | type: [Boolean, Number],
385 | default: true,
386 | }
387 | ```
388 | At times, when you configure a significant difference in distance between consecutive apexes, the presentation of the wave may not appear as smooth.
389 |
390 | To mitigate such outcomes, we perform certain calculations. However, you have the option to set it to `false` to disable this behavior.
391 |
392 | Alternatively, you can provide a `number` value between 0 and 1 to adjust the level of smoothness.
393 |
394 | ```html
395 |
396 |
397 |
410 |
411 | ```
412 |
413 |
414 |
415 |
416 |
417 | >⚠️ If the numerical disparities are indeed substantial, the extent of smoothing may still remain limited
418 |
419 |
420 |
421 | ### marquee
422 | ```typescript
423 | marquee: {
424 | type: Boolean,
425 | default: true,
426 | }
427 | ```
428 | When you use , the marquee animation effect will automatically activate. You can set it to `false` to deactivate the animation.
429 |
430 | Switch it from `true` to `false`, the animation will completely reset. If you only wish to pause the marquee, please set `marqueeSpeed` to zero.
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 | ### marqueeSpeed
439 | ```typescript
440 | marqueeSpeed: {
441 | type: Number,
442 | default: 2,
443 | }
444 | ```
445 | It accepts a `number` value ranging from -25 to 25 to control the speed of the marquee animation. When the value is greater than 0, the animation moves to the right; when less than 0, it moves to the left.
446 |
447 |
448 |
449 |
450 | ### transitionDuration
451 | ```typescript
452 | transitionDuration: {
453 | type: Number,
454 | default: 500,
455 | }
456 | ```
457 |
458 | Regardless of how you modify the current state of the wave ( ApexesSeriesTransform, Changing ApexesIndex or Changing Apex Value Directly... ), there will always be interpolation animations. You can pass a `number` value to adjust the transition duration. The unit is millisecond.
459 |
460 |
461 |
462 | ### apexesSeriesTransformAuto
463 | ```typescript
464 | apexesSeriesTransformAuto: {
465 | type: Boolean,
466 | default: true,
467 | }
468 | ```
469 | When you pass an ApexesSeries with a length greater than 1, VueSurf will transform the wave in accordance with the order of the ApexesSeries. You can set `apexesSeriesTransformAuto` to `false` to disable it.
470 |
471 |
472 |
473 |
474 | ### apexesSeriesTransformDuration
475 | ```typescript
476 | apexesSeriesTransformDuration: {
477 | type: Number,
478 | default: undefined,
479 | }
480 | ```
481 | This parameter only becomes meaningful when `apexesSeriesTransformAuto` is set to true.
482 |
483 | It determines how long it takes to replace the current wave with the next set of Apexes. The unit is millisecond.
484 |
485 | To achieve a seamless animation effect, when no value is specified, it defaults to being the same as `transitionDuration`. However, if `transitionDuration` is set to 0, `apexesSeriesTransformDuration` will automatically be set to 500.
486 |
487 |
488 |
489 | ### onApexesChanged
490 | ```typescript
491 | type ApexesChangedCallback = (
492 | currentApexes: Apex[],
493 | currentShape: WaveShape,
494 | ) => void;
495 |
496 | onApexesChanged: {
497 | type: Function as ApexesChangedCallback,
498 | default: undefined,
499 | }
500 | ```
501 | A callback function that is invoked when there is an update to the apexes.
502 |
503 |
504 |
505 | ## License
506 |
507 | [MIT](./LICENSE) License © 2023 [max.lee](https://github.com/f820602)
--------------------------------------------------------------------------------
/src/components/VueSurf.ts:
--------------------------------------------------------------------------------
1 | import {
2 | WaveProps,
3 | Apex,
4 | Apexes,
5 | WaveShape,
6 | WaveSide,
7 | LinearGradientColor,
8 | ApexesChangedCallback,
9 | } from "../types";
10 | import type { DefineComponent, PropType, StyleValue } from "vue";
11 | import {
12 | ref,
13 | computed,
14 | watch,
15 | defineComponent,
16 | h,
17 | onBeforeMount,
18 | onBeforeUnmount,
19 | } from "vue";
20 | import {
21 | errorText,
22 | lengthValidator,
23 | apexesSeriesValidator,
24 | shapeValidator,
25 | sideValidator,
26 | colorValidator,
27 | getLengthPixelNumber,
28 | debounce,
29 | } from "../utils";
30 | import { plotter } from "../plotter";
31 | import { useMarquee } from "../composables/marquee";
32 | import { useTransformation } from "../composables/transformation";
33 | import { useElementSize } from "@vueuse/core";
34 |
35 | export const VueSurf = defineComponent({
36 | name: "VueSurf",
37 | props: {
38 | width: {
39 | type: [Number, String],
40 | default: "100%",
41 | validator: lengthValidator,
42 | },
43 | color: {
44 | type: [String, Object] as PropType,
45 | default: "white",
46 | validator: colorValidator,
47 | },
48 | shape: {
49 | type: String as () => WaveShape,
50 | default: "wavy",
51 | validator: shapeValidator,
52 | },
53 | apexesSeries: {
54 | type: Array as () => Apexes[],
55 | required: true,
56 | validator: apexesSeriesValidator,
57 | },
58 | apexesIndex: {
59 | type: Number,
60 | default: undefined,
61 | },
62 | side: {
63 | type: String as () => WaveSide,
64 | default: "top",
65 | validator: sideValidator,
66 | },
67 | repeat: {
68 | type: Boolean,
69 | default: true,
70 | },
71 | closure: {
72 | type: Boolean,
73 | default: true,
74 | },
75 | smooth: {
76 | type: [Boolean, Number],
77 | default: true,
78 | },
79 | marquee: {
80 | type: Boolean,
81 | default: true,
82 | },
83 | marqueeSpeed: {
84 | type: Number,
85 | default: 2,
86 | },
87 | transitionDuration: {
88 | type: Number,
89 | default: 500,
90 | },
91 | apexesSeriesTransformAuto: {
92 | type: Boolean,
93 | default: true,
94 | },
95 | apexesSeriesTransformDuration: {
96 | type: Number,
97 | default: undefined,
98 | },
99 | onApexesChanged: {
100 | type: Function as PropType,
101 | default: undefined,
102 | },
103 | },
104 | setup(props) {
105 | const waveEl = ref(null);
106 | const { width: waveElWidth } = useElementSize(waveEl);
107 |
108 | const {
109 | start: startMarquee,
110 | stop: stopMarquee,
111 | resetSpeed,
112 | } = useMarquee(waveEl, props.marqueeSpeed);
113 | watch(() => props.marqueeSpeed, resetSpeed);
114 | watch(
115 | () => waveElWidth.value && props.marquee,
116 | (val) => {
117 | if (!val) stopMarquee();
118 | else startMarquee();
119 | },
120 | { immediate: true },
121 | );
122 |
123 | const {
124 | stamp: timestamp,
125 | pause: pauseApexesSeriesTransform,
126 | resume: resumeApexesSeriesTransform,
127 | } = useTransformation();
128 | watch(
129 | () =>
130 | props.apexesSeriesTransformAuto &&
131 | props.apexesSeries.length > 1 &&
132 | (props.apexesIndex === undefined || props.apexesIndex === null),
133 | (val) => {
134 | if (val) resumeApexesSeriesTransform();
135 | else pauseApexesSeriesTransform();
136 | },
137 | { immediate: true },
138 | );
139 |
140 | const isTransition = ref(true);
141 | const resetTransition = debounce(() => {
142 | isTransition.value = true;
143 | });
144 | function transitionToggle() {
145 | isTransition.value = false;
146 | resetTransition();
147 | }
148 | onBeforeMount(() => {
149 | window.addEventListener("resize", transitionToggle);
150 | });
151 | onBeforeUnmount(() => {
152 | window.removeEventListener("resize", transitionToggle);
153 | });
154 |
155 | function getClosureApexes(apexes: Apex[]): Apex[] {
156 | if (!props.closure) return apexes;
157 | const resultApexes = [...apexes];
158 | const firstApex = resultApexes[0];
159 | const lastApex = resultApexes[resultApexes.length - 1];
160 |
161 | const firstApexHeight = Array.isArray(firstApex)
162 | ? firstApex[1]
163 | : "height" in firstApex
164 | ? firstApex.height
165 | : 0;
166 | const lastApexDistance = Array.isArray(lastApex)
167 | ? lastApex[0]
168 | : "distance" in lastApex
169 | ? lastApex.distance
170 | : 0;
171 |
172 | resultApexes[0] = [0, firstApexHeight];
173 | resultApexes[resultApexes.length - 1] = [
174 | lastApexDistance,
175 | firstApexHeight,
176 | ];
177 |
178 | return resultApexes;
179 | }
180 | function getApexesPixelNumberArray(apexes: Apex[]): number[][] {
181 | return getClosureApexes(apexes).map((apex) => {
182 | if (Array.isArray(apex)) {
183 | return [
184 | getLengthPixelNumber(apex[0], waveElWidth.value),
185 | getLengthPixelNumber(apex[1], waveElWidth.value),
186 | ];
187 | } else if ("distance" in apex && "height" in apex) {
188 | return [
189 | getLengthPixelNumber(apex.distance, waveElWidth.value),
190 | getLengthPixelNumber(apex.height, waveElWidth.value),
191 | ];
192 | }
193 | console.warn(errorText.apexesFormat);
194 | return [1, 1];
195 | });
196 | }
197 | function getWaveLength(apexes: Apex[]): number {
198 | return getApexesPixelNumberArray(apexes).reduce((acc, [d]) => acc + d, 0);
199 | }
200 | function getWaveHeight(apexes: Apex[]): number {
201 | return Math.max(...getApexesPixelNumberArray(apexes).map(([, h]) => h));
202 | }
203 | function getRepeatTimes(apexes: Apex[]): number {
204 | if (!waveElWidth.value) return 0;
205 | return Math.ceil(waveElWidth.value / getWaveLength(apexes));
206 | }
207 |
208 | const counter = computed(() => {
209 | const duration =
210 | props.apexesSeriesTransformDuration || props.transitionDuration || 500;
211 | return Math.floor(timestamp.value / duration);
212 | });
213 | const apexesSeriesIndex = computed(() => {
214 | return props.apexesIndex !== undefined
215 | ? props.apexesIndex
216 | : counter.value % props.apexesSeries.length;
217 | });
218 |
219 | const currentApexes = computed(() => {
220 | const apexesSeries = props.apexesSeries[apexesSeriesIndex.value];
221 | if (Array.isArray(apexesSeries)) return apexesSeries;
222 | else if ("apexes" in apexesSeries) return apexesSeries.apexes;
223 | throw new Error(errorText.apexesSeriesFormat);
224 | });
225 | const currentShape = computed(() => {
226 | const apexesSeries = props.apexesSeries[apexesSeriesIndex.value];
227 | if ("shape" in apexesSeries)
228 | return apexesSeries.shape || props.shape || "wavy";
229 | return props.shape || "wavy";
230 | });
231 | const currentColor = computed(() => {
232 | const apexesSeries = props.apexesSeries[apexesSeriesIndex.value];
233 | if ("color" in apexesSeries) {
234 | return apexesSeries.color || props.color || "white";
235 | }
236 | return props.color || "white";
237 | });
238 |
239 | const apexesPixelSet = computed(() => {
240 | return getApexesPixelNumberArray(currentApexes.value);
241 | });
242 | const waveLength = computed(() => {
243 | return getWaveLength(currentApexes.value);
244 | });
245 | const waveHeight = computed(() => {
246 | return getWaveHeight(currentApexes.value);
247 | });
248 | const repeatTimes = computed(() => {
249 | return getRepeatTimes(currentApexes.value);
250 | });
251 |
252 | watch(
253 | () => currentApexes.value,
254 | async (newVal, oldVal) => {
255 | if (JSON.stringify(newVal) === JSON.stringify(oldVal)) return;
256 | if (oldVal && newVal.length !== oldVal.length) {
257 | console.warn(errorText.apexesLengthChanged);
258 | } else if (getRepeatTimes(oldVal) !== getRepeatTimes(newVal)) {
259 | console.warn(errorText.apexesTotalDistanceChanged);
260 | }
261 |
262 | props.onApexesChanged?.(
263 | [...newVal],
264 | currentShape.value,
265 | currentColor.value,
266 | );
267 | },
268 | { deep: true },
269 | );
270 |
271 | const repeatedApexesPixelSet = computed(() => {
272 | return Array.from({ length: repeatTimes.value })
273 | .map((_, i) => {
274 | if (i === 0) return apexesPixelSet.value;
275 | return props.repeat
276 | ? apexesPixelSet.value
277 | : apexesPixelSet.value.map(([d]) => {
278 | return [d, 0];
279 | });
280 | })
281 | .flat();
282 | });
283 |
284 | const wavePath = computed(() => {
285 | if (!repeatTimes.value) return "";
286 | return plotter({
287 | apexesPixelSet: repeatedApexesPixelSet.value,
288 | totalDistance: waveLength.value * repeatTimes.value * 5,
289 | waveHeight: waveHeight.value,
290 | side: props.side,
291 | shape: currentShape.value,
292 | smooth: props.smooth,
293 | });
294 | });
295 |
296 | const alignItems = computed(() => {
297 | return props.side === "bottom" ? "flex-start" : "flex-end";
298 | });
299 | const transition = computed(() => {
300 | if (!props.transitionDuration) return "";
301 | const timing = `${props.transitionDuration}ms linear`;
302 | const basicTransition = `height ${timing}, margin-top ${timing}, margin-bottom ${timing}`;
303 | return (
304 | (isTransition.value ? `width ${timing}, margin-left ${timing}, ` : "") +
305 | basicTransition
306 | );
307 | });
308 |
309 | const containerStyle = computed(() => {
310 | const height = `${waveHeight.value}px`;
311 | return {
312 | position: "relative",
313 | display: "flex",
314 | alignItems: alignItems.value,
315 | width: props.width + (typeof props.width === "number" ? "px" : ""),
316 | height,
317 | marginTop: props.side === "top" ? `-${height}` : "0px",
318 | marginBottom: props.side === "bottom" ? `-${height}` : "0px",
319 | transition: transition.value,
320 | overflow: "hidden",
321 | };
322 | });
323 |
324 | const repeatWrapperStyle = computed(() => {
325 | const totalWaveLength = waveLength.value * repeatTimes.value * 5;
326 | return {
327 | display: "flex",
328 | alignItems: alignItems.value,
329 | flexShrink: 0,
330 | width: `${totalWaveLength}px`,
331 | maxWidth: `${totalWaveLength}px`,
332 | height: "100%",
333 | marginLeft: `-${(totalWaveLength / 5) * 2}px`,
334 | transition: transition.value,
335 | overflow: "hidden",
336 | };
337 | });
338 |
339 | const svgStyle = computed(() => {
340 | return {
341 | flexShrink: 0,
342 | width: `${waveLength.value * repeatTimes.value * 5}px`,
343 | height: `${waveHeight.value}px`,
344 | transition: transition.value,
345 | };
346 | });
347 |
348 | const linearGradientStyle = computed(() => {
349 | return {
350 | transition: !props.transitionDuration
351 | ? ""
352 | : `${props.transitionDuration}ms linear`,
353 | };
354 | });
355 |
356 | const stopStyle = computed(() => {
357 | return {
358 | transition: !props.transitionDuration
359 | ? ""
360 | : `${props.transitionDuration}ms linear`,
361 | };
362 | });
363 |
364 | const pathColor = computed(() => {
365 | if (typeof currentColor.value === "object") return currentColor.value;
366 | return {
367 | name: `vue-surf-${currentColor.value}`,
368 | steps: [
369 | { offset: 0, color: currentColor.value, opacity: 1 },
370 | { offset: 1, color: currentColor.value, opacity: 1 },
371 | ],
372 | };
373 | });
374 |
375 | const pathStyle = computed(() => {
376 | return {
377 | fill: `url(#${pathColor.value.name})`,
378 | transition: !props.transitionDuration
379 | ? ""
380 | : `${props.transitionDuration}ms linear`,
381 | };
382 | });
383 |
384 | return {
385 | waveEl,
386 | waveElWidth,
387 | repeatTimes,
388 | containerStyle,
389 | repeatWrapperStyle,
390 | svgStyle,
391 | linearGradientStyle,
392 | stopStyle,
393 | pathStyle,
394 | pathColor,
395 | wavePath,
396 | };
397 | },
398 | render() {
399 | const {
400 | waveElWidth,
401 | repeatTimes,
402 | containerStyle,
403 | repeatWrapperStyle,
404 | svgStyle,
405 | linearGradientStyle,
406 | stopStyle,
407 | pathStyle,
408 | pathColor,
409 | wavePath,
410 | } = this;
411 |
412 | return h(
413 | "div",
414 | { ref: "waveEl", class: "vue-wave", style: containerStyle },
415 | [
416 | !!waveElWidth &&
417 | h(
418 | "div",
419 | { class: "vue-wave__repeat-wrapper", style: repeatWrapperStyle },
420 | [
421 | repeatTimes > 0 &&
422 | h(
423 | "svg",
424 | {
425 | xmlns: "http://www.w3.org/2000/svg",
426 | viewBox: "0,0,100,100",
427 | preserveAspectRatio: "none",
428 | style: svgStyle,
429 | },
430 | [
431 | h("defs", [
432 | h(
433 | "linearGradient",
434 | {
435 | id: pathColor.name,
436 | gradientTransform: `rotate(${
437 | pathColor.rotate || 0
438 | }, .5, .5)`,
439 | style: linearGradientStyle,
440 | },
441 | [
442 | pathColor.steps.length > 0 &&
443 | Array.from(pathColor.steps).map(
444 | ({ offset, color, opacity }) => {
445 | return h("stop", {
446 | offset,
447 | style: {
448 | stopColor: color,
449 | stopOpacity:
450 | opacity === undefined ? 1 : opacity,
451 | ...stopStyle,
452 | },
453 | });
454 | },
455 | ),
456 | ],
457 | ),
458 | ]),
459 | wavePath &&
460 | Array.from({ length: 5 }).map((_, i) =>
461 | h("path", {
462 | d: wavePath,
463 | "vector-effect": "non-scaling-stroke",
464 | style: {
465 | ...pathStyle,
466 | transform: `translateX(${20 * i}%)`,
467 | },
468 | }),
469 | ),
470 | ],
471 | ),
472 | ],
473 | ),
474 | ],
475 | );
476 | },
477 | }) as DefineComponent;
478 |
--------------------------------------------------------------------------------
/playground/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "playground",
3 | "lockfileVersion": 2,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "dependencies": {
8 | "vue": "^3.3.4"
9 | },
10 | "devDependencies": {
11 | "@vitejs/plugin-vue": "^4.2.3",
12 | "typescript": "^5.0.2",
13 | "vite": "^4.4.5",
14 | "vue-tsc": "^1.8.5"
15 | }
16 | },
17 | "node_modules/@babel/parser": {
18 | "version": "7.22.16",
19 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz",
20 | "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==",
21 | "bin": {
22 | "parser": "bin/babel-parser.js"
23 | },
24 | "engines": {
25 | "node": ">=6.0.0"
26 | }
27 | },
28 | "node_modules/@esbuild/android-arm": {
29 | "version": "0.18.20",
30 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
31 | "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
32 | "cpu": [
33 | "arm"
34 | ],
35 | "dev": true,
36 | "optional": true,
37 | "os": [
38 | "android"
39 | ],
40 | "engines": {
41 | "node": ">=12"
42 | }
43 | },
44 | "node_modules/@esbuild/android-arm64": {
45 | "version": "0.18.20",
46 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
47 | "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
48 | "cpu": [
49 | "arm64"
50 | ],
51 | "dev": true,
52 | "optional": true,
53 | "os": [
54 | "android"
55 | ],
56 | "engines": {
57 | "node": ">=12"
58 | }
59 | },
60 | "node_modules/@esbuild/android-x64": {
61 | "version": "0.18.20",
62 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
63 | "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
64 | "cpu": [
65 | "x64"
66 | ],
67 | "dev": true,
68 | "optional": true,
69 | "os": [
70 | "android"
71 | ],
72 | "engines": {
73 | "node": ">=12"
74 | }
75 | },
76 | "node_modules/@esbuild/darwin-arm64": {
77 | "version": "0.18.20",
78 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
79 | "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
80 | "cpu": [
81 | "arm64"
82 | ],
83 | "dev": true,
84 | "optional": true,
85 | "os": [
86 | "darwin"
87 | ],
88 | "engines": {
89 | "node": ">=12"
90 | }
91 | },
92 | "node_modules/@esbuild/darwin-x64": {
93 | "version": "0.18.20",
94 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
95 | "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
96 | "cpu": [
97 | "x64"
98 | ],
99 | "dev": true,
100 | "optional": true,
101 | "os": [
102 | "darwin"
103 | ],
104 | "engines": {
105 | "node": ">=12"
106 | }
107 | },
108 | "node_modules/@esbuild/freebsd-arm64": {
109 | "version": "0.18.20",
110 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
111 | "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
112 | "cpu": [
113 | "arm64"
114 | ],
115 | "dev": true,
116 | "optional": true,
117 | "os": [
118 | "freebsd"
119 | ],
120 | "engines": {
121 | "node": ">=12"
122 | }
123 | },
124 | "node_modules/@esbuild/freebsd-x64": {
125 | "version": "0.18.20",
126 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
127 | "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
128 | "cpu": [
129 | "x64"
130 | ],
131 | "dev": true,
132 | "optional": true,
133 | "os": [
134 | "freebsd"
135 | ],
136 | "engines": {
137 | "node": ">=12"
138 | }
139 | },
140 | "node_modules/@esbuild/linux-arm": {
141 | "version": "0.18.20",
142 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
143 | "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
144 | "cpu": [
145 | "arm"
146 | ],
147 | "dev": true,
148 | "optional": true,
149 | "os": [
150 | "linux"
151 | ],
152 | "engines": {
153 | "node": ">=12"
154 | }
155 | },
156 | "node_modules/@esbuild/linux-arm64": {
157 | "version": "0.18.20",
158 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
159 | "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
160 | "cpu": [
161 | "arm64"
162 | ],
163 | "dev": true,
164 | "optional": true,
165 | "os": [
166 | "linux"
167 | ],
168 | "engines": {
169 | "node": ">=12"
170 | }
171 | },
172 | "node_modules/@esbuild/linux-ia32": {
173 | "version": "0.18.20",
174 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
175 | "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
176 | "cpu": [
177 | "ia32"
178 | ],
179 | "dev": true,
180 | "optional": true,
181 | "os": [
182 | "linux"
183 | ],
184 | "engines": {
185 | "node": ">=12"
186 | }
187 | },
188 | "node_modules/@esbuild/linux-loong64": {
189 | "version": "0.18.20",
190 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
191 | "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
192 | "cpu": [
193 | "loong64"
194 | ],
195 | "dev": true,
196 | "optional": true,
197 | "os": [
198 | "linux"
199 | ],
200 | "engines": {
201 | "node": ">=12"
202 | }
203 | },
204 | "node_modules/@esbuild/linux-mips64el": {
205 | "version": "0.18.20",
206 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
207 | "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
208 | "cpu": [
209 | "mips64el"
210 | ],
211 | "dev": true,
212 | "optional": true,
213 | "os": [
214 | "linux"
215 | ],
216 | "engines": {
217 | "node": ">=12"
218 | }
219 | },
220 | "node_modules/@esbuild/linux-ppc64": {
221 | "version": "0.18.20",
222 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
223 | "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
224 | "cpu": [
225 | "ppc64"
226 | ],
227 | "dev": true,
228 | "optional": true,
229 | "os": [
230 | "linux"
231 | ],
232 | "engines": {
233 | "node": ">=12"
234 | }
235 | },
236 | "node_modules/@esbuild/linux-riscv64": {
237 | "version": "0.18.20",
238 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
239 | "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
240 | "cpu": [
241 | "riscv64"
242 | ],
243 | "dev": true,
244 | "optional": true,
245 | "os": [
246 | "linux"
247 | ],
248 | "engines": {
249 | "node": ">=12"
250 | }
251 | },
252 | "node_modules/@esbuild/linux-s390x": {
253 | "version": "0.18.20",
254 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
255 | "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
256 | "cpu": [
257 | "s390x"
258 | ],
259 | "dev": true,
260 | "optional": true,
261 | "os": [
262 | "linux"
263 | ],
264 | "engines": {
265 | "node": ">=12"
266 | }
267 | },
268 | "node_modules/@esbuild/linux-x64": {
269 | "version": "0.18.20",
270 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
271 | "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
272 | "cpu": [
273 | "x64"
274 | ],
275 | "dev": true,
276 | "optional": true,
277 | "os": [
278 | "linux"
279 | ],
280 | "engines": {
281 | "node": ">=12"
282 | }
283 | },
284 | "node_modules/@esbuild/netbsd-x64": {
285 | "version": "0.18.20",
286 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
287 | "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
288 | "cpu": [
289 | "x64"
290 | ],
291 | "dev": true,
292 | "optional": true,
293 | "os": [
294 | "netbsd"
295 | ],
296 | "engines": {
297 | "node": ">=12"
298 | }
299 | },
300 | "node_modules/@esbuild/openbsd-x64": {
301 | "version": "0.18.20",
302 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
303 | "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
304 | "cpu": [
305 | "x64"
306 | ],
307 | "dev": true,
308 | "optional": true,
309 | "os": [
310 | "openbsd"
311 | ],
312 | "engines": {
313 | "node": ">=12"
314 | }
315 | },
316 | "node_modules/@esbuild/sunos-x64": {
317 | "version": "0.18.20",
318 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
319 | "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
320 | "cpu": [
321 | "x64"
322 | ],
323 | "dev": true,
324 | "optional": true,
325 | "os": [
326 | "sunos"
327 | ],
328 | "engines": {
329 | "node": ">=12"
330 | }
331 | },
332 | "node_modules/@esbuild/win32-arm64": {
333 | "version": "0.18.20",
334 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
335 | "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
336 | "cpu": [
337 | "arm64"
338 | ],
339 | "dev": true,
340 | "optional": true,
341 | "os": [
342 | "win32"
343 | ],
344 | "engines": {
345 | "node": ">=12"
346 | }
347 | },
348 | "node_modules/@esbuild/win32-ia32": {
349 | "version": "0.18.20",
350 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
351 | "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
352 | "cpu": [
353 | "ia32"
354 | ],
355 | "dev": true,
356 | "optional": true,
357 | "os": [
358 | "win32"
359 | ],
360 | "engines": {
361 | "node": ">=12"
362 | }
363 | },
364 | "node_modules/@esbuild/win32-x64": {
365 | "version": "0.18.20",
366 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
367 | "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
368 | "cpu": [
369 | "x64"
370 | ],
371 | "dev": true,
372 | "optional": true,
373 | "os": [
374 | "win32"
375 | ],
376 | "engines": {
377 | "node": ">=12"
378 | }
379 | },
380 | "node_modules/@jridgewell/sourcemap-codec": {
381 | "version": "1.4.15",
382 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
383 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
384 | },
385 | "node_modules/@types/node": {
386 | "version": "20.6.1",
387 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.1.tgz",
388 | "integrity": "sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g==",
389 | "dev": true,
390 | "optional": true,
391 | "peer": true
392 | },
393 | "node_modules/@vitejs/plugin-vue": {
394 | "version": "4.3.4",
395 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
396 | "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
397 | "dev": true,
398 | "engines": {
399 | "node": "^14.18.0 || >=16.0.0"
400 | },
401 | "peerDependencies": {
402 | "vite": "^4.0.0",
403 | "vue": "^3.2.25"
404 | }
405 | },
406 | "node_modules/@volar/language-core": {
407 | "version": "1.10.1",
408 | "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz",
409 | "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==",
410 | "dev": true,
411 | "dependencies": {
412 | "@volar/source-map": "1.10.1"
413 | }
414 | },
415 | "node_modules/@volar/source-map": {
416 | "version": "1.10.1",
417 | "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz",
418 | "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==",
419 | "dev": true,
420 | "dependencies": {
421 | "muggle-string": "^0.3.1"
422 | }
423 | },
424 | "node_modules/@volar/typescript": {
425 | "version": "1.10.1",
426 | "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz",
427 | "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==",
428 | "dev": true,
429 | "dependencies": {
430 | "@volar/language-core": "1.10.1"
431 | }
432 | },
433 | "node_modules/@vue/compiler-core": {
434 | "version": "3.3.4",
435 | "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
436 | "integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
437 | "dependencies": {
438 | "@babel/parser": "^7.21.3",
439 | "@vue/shared": "3.3.4",
440 | "estree-walker": "^2.0.2",
441 | "source-map-js": "^1.0.2"
442 | }
443 | },
444 | "node_modules/@vue/compiler-dom": {
445 | "version": "3.3.4",
446 | "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
447 | "integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
448 | "dependencies": {
449 | "@vue/compiler-core": "3.3.4",
450 | "@vue/shared": "3.3.4"
451 | }
452 | },
453 | "node_modules/@vue/compiler-sfc": {
454 | "version": "3.3.4",
455 | "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
456 | "integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
457 | "dependencies": {
458 | "@babel/parser": "^7.20.15",
459 | "@vue/compiler-core": "3.3.4",
460 | "@vue/compiler-dom": "3.3.4",
461 | "@vue/compiler-ssr": "3.3.4",
462 | "@vue/reactivity-transform": "3.3.4",
463 | "@vue/shared": "3.3.4",
464 | "estree-walker": "^2.0.2",
465 | "magic-string": "^0.30.0",
466 | "postcss": "^8.1.10",
467 | "source-map-js": "^1.0.2"
468 | }
469 | },
470 | "node_modules/@vue/compiler-ssr": {
471 | "version": "3.3.4",
472 | "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
473 | "integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
474 | "dependencies": {
475 | "@vue/compiler-dom": "3.3.4",
476 | "@vue/shared": "3.3.4"
477 | }
478 | },
479 | "node_modules/@vue/language-core": {
480 | "version": "1.8.11",
481 | "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.11.tgz",
482 | "integrity": "sha512-+MZOBGqGwfld6hpo0DB47x8eNM0dNqk15ZdfOhj19CpvuYuOWCeVdOEGZunKDyo3QLkTn3kLOSysJzg7FDOQBA==",
483 | "dev": true,
484 | "dependencies": {
485 | "@volar/language-core": "~1.10.0",
486 | "@volar/source-map": "~1.10.0",
487 | "@vue/compiler-dom": "^3.3.0",
488 | "@vue/reactivity": "^3.3.0",
489 | "@vue/shared": "^3.3.0",
490 | "minimatch": "^9.0.0",
491 | "muggle-string": "^0.3.1",
492 | "vue-template-compiler": "^2.7.14"
493 | },
494 | "peerDependencies": {
495 | "typescript": "*"
496 | },
497 | "peerDependenciesMeta": {
498 | "typescript": {
499 | "optional": true
500 | }
501 | }
502 | },
503 | "node_modules/@vue/reactivity": {
504 | "version": "3.3.4",
505 | "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
506 | "integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
507 | "dependencies": {
508 | "@vue/shared": "3.3.4"
509 | }
510 | },
511 | "node_modules/@vue/reactivity-transform": {
512 | "version": "3.3.4",
513 | "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
514 | "integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
515 | "dependencies": {
516 | "@babel/parser": "^7.20.15",
517 | "@vue/compiler-core": "3.3.4",
518 | "@vue/shared": "3.3.4",
519 | "estree-walker": "^2.0.2",
520 | "magic-string": "^0.30.0"
521 | }
522 | },
523 | "node_modules/@vue/runtime-core": {
524 | "version": "3.3.4",
525 | "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
526 | "integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
527 | "dependencies": {
528 | "@vue/reactivity": "3.3.4",
529 | "@vue/shared": "3.3.4"
530 | }
531 | },
532 | "node_modules/@vue/runtime-dom": {
533 | "version": "3.3.4",
534 | "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
535 | "integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
536 | "dependencies": {
537 | "@vue/runtime-core": "3.3.4",
538 | "@vue/shared": "3.3.4",
539 | "csstype": "^3.1.1"
540 | }
541 | },
542 | "node_modules/@vue/server-renderer": {
543 | "version": "3.3.4",
544 | "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
545 | "integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
546 | "dependencies": {
547 | "@vue/compiler-ssr": "3.3.4",
548 | "@vue/shared": "3.3.4"
549 | },
550 | "peerDependencies": {
551 | "vue": "3.3.4"
552 | }
553 | },
554 | "node_modules/@vue/shared": {
555 | "version": "3.3.4",
556 | "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
557 | "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
558 | },
559 | "node_modules/@vue/typescript": {
560 | "version": "1.8.11",
561 | "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.11.tgz",
562 | "integrity": "sha512-skUmMDiPUUtu1flPmf2YybF+PX8IzBtMioQOaNn6Ck/RhdrPJGj1AX/7s3Buf9G6ln+/KHR1XQuti/FFxw5XVA==",
563 | "dev": true,
564 | "dependencies": {
565 | "@volar/typescript": "~1.10.0",
566 | "@vue/language-core": "1.8.11"
567 | }
568 | },
569 | "node_modules/anymatch": {
570 | "version": "3.1.3",
571 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
572 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
573 | "dev": true,
574 | "optional": true,
575 | "peer": true,
576 | "dependencies": {
577 | "normalize-path": "^3.0.0",
578 | "picomatch": "^2.0.4"
579 | },
580 | "engines": {
581 | "node": ">= 8"
582 | }
583 | },
584 | "node_modules/balanced-match": {
585 | "version": "1.0.2",
586 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
587 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
588 | "dev": true
589 | },
590 | "node_modules/binary-extensions": {
591 | "version": "2.2.0",
592 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
593 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
594 | "dev": true,
595 | "optional": true,
596 | "peer": true,
597 | "engines": {
598 | "node": ">=8"
599 | }
600 | },
601 | "node_modules/brace-expansion": {
602 | "version": "2.0.1",
603 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
604 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
605 | "dev": true,
606 | "dependencies": {
607 | "balanced-match": "^1.0.0"
608 | }
609 | },
610 | "node_modules/braces": {
611 | "version": "3.0.2",
612 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
613 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
614 | "dev": true,
615 | "optional": true,
616 | "peer": true,
617 | "dependencies": {
618 | "fill-range": "^7.0.1"
619 | },
620 | "engines": {
621 | "node": ">=8"
622 | }
623 | },
624 | "node_modules/chokidar": {
625 | "version": "3.5.3",
626 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
627 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
628 | "dev": true,
629 | "funding": [
630 | {
631 | "type": "individual",
632 | "url": "https://paulmillr.com/funding/"
633 | }
634 | ],
635 | "optional": true,
636 | "peer": true,
637 | "dependencies": {
638 | "anymatch": "~3.1.2",
639 | "braces": "~3.0.2",
640 | "glob-parent": "~5.1.2",
641 | "is-binary-path": "~2.1.0",
642 | "is-glob": "~4.0.1",
643 | "normalize-path": "~3.0.0",
644 | "readdirp": "~3.6.0"
645 | },
646 | "engines": {
647 | "node": ">= 8.10.0"
648 | },
649 | "optionalDependencies": {
650 | "fsevents": "~2.3.2"
651 | }
652 | },
653 | "node_modules/csstype": {
654 | "version": "3.1.2",
655 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
656 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
657 | },
658 | "node_modules/de-indent": {
659 | "version": "1.0.2",
660 | "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
661 | "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
662 | "dev": true
663 | },
664 | "node_modules/esbuild": {
665 | "version": "0.18.20",
666 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
667 | "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
668 | "dev": true,
669 | "hasInstallScript": true,
670 | "bin": {
671 | "esbuild": "bin/esbuild"
672 | },
673 | "engines": {
674 | "node": ">=12"
675 | },
676 | "optionalDependencies": {
677 | "@esbuild/android-arm": "0.18.20",
678 | "@esbuild/android-arm64": "0.18.20",
679 | "@esbuild/android-x64": "0.18.20",
680 | "@esbuild/darwin-arm64": "0.18.20",
681 | "@esbuild/darwin-x64": "0.18.20",
682 | "@esbuild/freebsd-arm64": "0.18.20",
683 | "@esbuild/freebsd-x64": "0.18.20",
684 | "@esbuild/linux-arm": "0.18.20",
685 | "@esbuild/linux-arm64": "0.18.20",
686 | "@esbuild/linux-ia32": "0.18.20",
687 | "@esbuild/linux-loong64": "0.18.20",
688 | "@esbuild/linux-mips64el": "0.18.20",
689 | "@esbuild/linux-ppc64": "0.18.20",
690 | "@esbuild/linux-riscv64": "0.18.20",
691 | "@esbuild/linux-s390x": "0.18.20",
692 | "@esbuild/linux-x64": "0.18.20",
693 | "@esbuild/netbsd-x64": "0.18.20",
694 | "@esbuild/openbsd-x64": "0.18.20",
695 | "@esbuild/sunos-x64": "0.18.20",
696 | "@esbuild/win32-arm64": "0.18.20",
697 | "@esbuild/win32-ia32": "0.18.20",
698 | "@esbuild/win32-x64": "0.18.20"
699 | }
700 | },
701 | "node_modules/estree-walker": {
702 | "version": "2.0.2",
703 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
704 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
705 | },
706 | "node_modules/fill-range": {
707 | "version": "7.0.1",
708 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
709 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
710 | "dev": true,
711 | "optional": true,
712 | "peer": true,
713 | "dependencies": {
714 | "to-regex-range": "^5.0.1"
715 | },
716 | "engines": {
717 | "node": ">=8"
718 | }
719 | },
720 | "node_modules/fsevents": {
721 | "version": "2.3.3",
722 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
723 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
724 | "dev": true,
725 | "hasInstallScript": true,
726 | "optional": true,
727 | "os": [
728 | "darwin"
729 | ],
730 | "engines": {
731 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
732 | }
733 | },
734 | "node_modules/glob-parent": {
735 | "version": "5.1.2",
736 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
737 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
738 | "dev": true,
739 | "optional": true,
740 | "peer": true,
741 | "dependencies": {
742 | "is-glob": "^4.0.1"
743 | },
744 | "engines": {
745 | "node": ">= 6"
746 | }
747 | },
748 | "node_modules/he": {
749 | "version": "1.2.0",
750 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
751 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
752 | "dev": true,
753 | "bin": {
754 | "he": "bin/he"
755 | }
756 | },
757 | "node_modules/immutable": {
758 | "version": "4.3.4",
759 | "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
760 | "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
761 | "dev": true,
762 | "optional": true,
763 | "peer": true
764 | },
765 | "node_modules/is-binary-path": {
766 | "version": "2.1.0",
767 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
768 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
769 | "dev": true,
770 | "optional": true,
771 | "peer": true,
772 | "dependencies": {
773 | "binary-extensions": "^2.0.0"
774 | },
775 | "engines": {
776 | "node": ">=8"
777 | }
778 | },
779 | "node_modules/is-extglob": {
780 | "version": "2.1.1",
781 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
782 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
783 | "dev": true,
784 | "optional": true,
785 | "peer": true,
786 | "engines": {
787 | "node": ">=0.10.0"
788 | }
789 | },
790 | "node_modules/is-glob": {
791 | "version": "4.0.3",
792 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
793 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
794 | "dev": true,
795 | "optional": true,
796 | "peer": true,
797 | "dependencies": {
798 | "is-extglob": "^2.1.1"
799 | },
800 | "engines": {
801 | "node": ">=0.10.0"
802 | }
803 | },
804 | "node_modules/is-number": {
805 | "version": "7.0.0",
806 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
807 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
808 | "dev": true,
809 | "optional": true,
810 | "peer": true,
811 | "engines": {
812 | "node": ">=0.12.0"
813 | }
814 | },
815 | "node_modules/lru-cache": {
816 | "version": "6.0.0",
817 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
818 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
819 | "dev": true,
820 | "dependencies": {
821 | "yallist": "^4.0.0"
822 | },
823 | "engines": {
824 | "node": ">=10"
825 | }
826 | },
827 | "node_modules/magic-string": {
828 | "version": "0.30.3",
829 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz",
830 | "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==",
831 | "dependencies": {
832 | "@jridgewell/sourcemap-codec": "^1.4.15"
833 | },
834 | "engines": {
835 | "node": ">=12"
836 | }
837 | },
838 | "node_modules/minimatch": {
839 | "version": "9.0.3",
840 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
841 | "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
842 | "dev": true,
843 | "dependencies": {
844 | "brace-expansion": "^2.0.1"
845 | },
846 | "engines": {
847 | "node": ">=16 || 14 >=14.17"
848 | },
849 | "funding": {
850 | "url": "https://github.com/sponsors/isaacs"
851 | }
852 | },
853 | "node_modules/muggle-string": {
854 | "version": "0.3.1",
855 | "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz",
856 | "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==",
857 | "dev": true
858 | },
859 | "node_modules/nanoid": {
860 | "version": "3.3.6",
861 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
862 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
863 | "funding": [
864 | {
865 | "type": "github",
866 | "url": "https://github.com/sponsors/ai"
867 | }
868 | ],
869 | "bin": {
870 | "nanoid": "bin/nanoid.cjs"
871 | },
872 | "engines": {
873 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
874 | }
875 | },
876 | "node_modules/normalize-path": {
877 | "version": "3.0.0",
878 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
879 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
880 | "dev": true,
881 | "optional": true,
882 | "peer": true,
883 | "engines": {
884 | "node": ">=0.10.0"
885 | }
886 | },
887 | "node_modules/picocolors": {
888 | "version": "1.0.0",
889 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
890 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
891 | },
892 | "node_modules/picomatch": {
893 | "version": "2.3.1",
894 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
895 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
896 | "dev": true,
897 | "optional": true,
898 | "peer": true,
899 | "engines": {
900 | "node": ">=8.6"
901 | },
902 | "funding": {
903 | "url": "https://github.com/sponsors/jonschlinkert"
904 | }
905 | },
906 | "node_modules/postcss": {
907 | "version": "8.4.29",
908 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
909 | "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
910 | "funding": [
911 | {
912 | "type": "opencollective",
913 | "url": "https://opencollective.com/postcss/"
914 | },
915 | {
916 | "type": "tidelift",
917 | "url": "https://tidelift.com/funding/github/npm/postcss"
918 | },
919 | {
920 | "type": "github",
921 | "url": "https://github.com/sponsors/ai"
922 | }
923 | ],
924 | "dependencies": {
925 | "nanoid": "^3.3.6",
926 | "picocolors": "^1.0.0",
927 | "source-map-js": "^1.0.2"
928 | },
929 | "engines": {
930 | "node": "^10 || ^12 || >=14"
931 | }
932 | },
933 | "node_modules/readdirp": {
934 | "version": "3.6.0",
935 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
936 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
937 | "dev": true,
938 | "optional": true,
939 | "peer": true,
940 | "dependencies": {
941 | "picomatch": "^2.2.1"
942 | },
943 | "engines": {
944 | "node": ">=8.10.0"
945 | }
946 | },
947 | "node_modules/rollup": {
948 | "version": "3.29.2",
949 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.2.tgz",
950 | "integrity": "sha512-CJouHoZ27v6siztc21eEQGo0kIcE5D1gVPA571ez0mMYb25LGYGKnVNXpEj5MGlepmDWGXNjDB5q7uNiPHC11A==",
951 | "dev": true,
952 | "bin": {
953 | "rollup": "dist/bin/rollup"
954 | },
955 | "engines": {
956 | "node": ">=14.18.0",
957 | "npm": ">=8.0.0"
958 | },
959 | "optionalDependencies": {
960 | "fsevents": "~2.3.2"
961 | }
962 | },
963 | "node_modules/sass": {
964 | "version": "1.67.0",
965 | "resolved": "https://registry.npmjs.org/sass/-/sass-1.67.0.tgz",
966 | "integrity": "sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A==",
967 | "dev": true,
968 | "optional": true,
969 | "peer": true,
970 | "dependencies": {
971 | "chokidar": ">=3.0.0 <4.0.0",
972 | "immutable": "^4.0.0",
973 | "source-map-js": ">=0.6.2 <2.0.0"
974 | },
975 | "bin": {
976 | "sass": "sass.js"
977 | },
978 | "engines": {
979 | "node": ">=14.0.0"
980 | }
981 | },
982 | "node_modules/semver": {
983 | "version": "7.5.4",
984 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
985 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
986 | "dev": true,
987 | "dependencies": {
988 | "lru-cache": "^6.0.0"
989 | },
990 | "bin": {
991 | "semver": "bin/semver.js"
992 | },
993 | "engines": {
994 | "node": ">=10"
995 | }
996 | },
997 | "node_modules/source-map-js": {
998 | "version": "1.0.2",
999 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
1000 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
1001 | "engines": {
1002 | "node": ">=0.10.0"
1003 | }
1004 | },
1005 | "node_modules/to-regex-range": {
1006 | "version": "5.0.1",
1007 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1008 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1009 | "dev": true,
1010 | "optional": true,
1011 | "peer": true,
1012 | "dependencies": {
1013 | "is-number": "^7.0.0"
1014 | },
1015 | "engines": {
1016 | "node": ">=8.0"
1017 | }
1018 | },
1019 | "node_modules/typescript": {
1020 | "version": "5.2.2",
1021 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
1022 | "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
1023 | "dev": true,
1024 | "bin": {
1025 | "tsc": "bin/tsc",
1026 | "tsserver": "bin/tsserver"
1027 | },
1028 | "engines": {
1029 | "node": ">=14.17"
1030 | }
1031 | },
1032 | "node_modules/vite": {
1033 | "version": "4.4.9",
1034 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
1035 | "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
1036 | "dev": true,
1037 | "dependencies": {
1038 | "esbuild": "^0.18.10",
1039 | "postcss": "^8.4.27",
1040 | "rollup": "^3.27.1"
1041 | },
1042 | "bin": {
1043 | "vite": "bin/vite.js"
1044 | },
1045 | "engines": {
1046 | "node": "^14.18.0 || >=16.0.0"
1047 | },
1048 | "funding": {
1049 | "url": "https://github.com/vitejs/vite?sponsor=1"
1050 | },
1051 | "optionalDependencies": {
1052 | "fsevents": "~2.3.2"
1053 | },
1054 | "peerDependencies": {
1055 | "@types/node": ">= 14",
1056 | "less": "*",
1057 | "lightningcss": "^1.21.0",
1058 | "sass": "*",
1059 | "stylus": "*",
1060 | "sugarss": "*",
1061 | "terser": "^5.4.0"
1062 | },
1063 | "peerDependenciesMeta": {
1064 | "@types/node": {
1065 | "optional": true
1066 | },
1067 | "less": {
1068 | "optional": true
1069 | },
1070 | "lightningcss": {
1071 | "optional": true
1072 | },
1073 | "sass": {
1074 | "optional": true
1075 | },
1076 | "stylus": {
1077 | "optional": true
1078 | },
1079 | "sugarss": {
1080 | "optional": true
1081 | },
1082 | "terser": {
1083 | "optional": true
1084 | }
1085 | }
1086 | },
1087 | "node_modules/vue": {
1088 | "version": "3.3.4",
1089 | "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
1090 | "integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
1091 | "dependencies": {
1092 | "@vue/compiler-dom": "3.3.4",
1093 | "@vue/compiler-sfc": "3.3.4",
1094 | "@vue/runtime-dom": "3.3.4",
1095 | "@vue/server-renderer": "3.3.4",
1096 | "@vue/shared": "3.3.4"
1097 | }
1098 | },
1099 | "node_modules/vue-template-compiler": {
1100 | "version": "2.7.14",
1101 | "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
1102 | "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
1103 | "dev": true,
1104 | "dependencies": {
1105 | "de-indent": "^1.0.2",
1106 | "he": "^1.2.0"
1107 | }
1108 | },
1109 | "node_modules/vue-tsc": {
1110 | "version": "1.8.11",
1111 | "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.11.tgz",
1112 | "integrity": "sha512-BzfiMdPqDHBlysx4g26NkfVHSQwGD/lTRausmxN9sFyjXz34OWfsbkh0YsVkX84Hu65In1fFlxHiG39Tr4Vojg==",
1113 | "dev": true,
1114 | "dependencies": {
1115 | "@vue/language-core": "1.8.11",
1116 | "@vue/typescript": "1.8.11",
1117 | "semver": "^7.3.8"
1118 | },
1119 | "bin": {
1120 | "vue-tsc": "bin/vue-tsc.js"
1121 | },
1122 | "peerDependencies": {
1123 | "typescript": "*"
1124 | }
1125 | },
1126 | "node_modules/yallist": {
1127 | "version": "4.0.0",
1128 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1129 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
1130 | "dev": true
1131 | }
1132 | },
1133 | "dependencies": {
1134 | "@babel/parser": {
1135 | "version": "7.22.16",
1136 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz",
1137 | "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA=="
1138 | },
1139 | "@esbuild/android-arm": {
1140 | "version": "0.18.20",
1141 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
1142 | "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
1143 | "dev": true,
1144 | "optional": true
1145 | },
1146 | "@esbuild/android-arm64": {
1147 | "version": "0.18.20",
1148 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
1149 | "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
1150 | "dev": true,
1151 | "optional": true
1152 | },
1153 | "@esbuild/android-x64": {
1154 | "version": "0.18.20",
1155 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
1156 | "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
1157 | "dev": true,
1158 | "optional": true
1159 | },
1160 | "@esbuild/darwin-arm64": {
1161 | "version": "0.18.20",
1162 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
1163 | "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
1164 | "dev": true,
1165 | "optional": true
1166 | },
1167 | "@esbuild/darwin-x64": {
1168 | "version": "0.18.20",
1169 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
1170 | "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
1171 | "dev": true,
1172 | "optional": true
1173 | },
1174 | "@esbuild/freebsd-arm64": {
1175 | "version": "0.18.20",
1176 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
1177 | "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
1178 | "dev": true,
1179 | "optional": true
1180 | },
1181 | "@esbuild/freebsd-x64": {
1182 | "version": "0.18.20",
1183 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
1184 | "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
1185 | "dev": true,
1186 | "optional": true
1187 | },
1188 | "@esbuild/linux-arm": {
1189 | "version": "0.18.20",
1190 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
1191 | "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
1192 | "dev": true,
1193 | "optional": true
1194 | },
1195 | "@esbuild/linux-arm64": {
1196 | "version": "0.18.20",
1197 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
1198 | "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
1199 | "dev": true,
1200 | "optional": true
1201 | },
1202 | "@esbuild/linux-ia32": {
1203 | "version": "0.18.20",
1204 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
1205 | "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
1206 | "dev": true,
1207 | "optional": true
1208 | },
1209 | "@esbuild/linux-loong64": {
1210 | "version": "0.18.20",
1211 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
1212 | "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
1213 | "dev": true,
1214 | "optional": true
1215 | },
1216 | "@esbuild/linux-mips64el": {
1217 | "version": "0.18.20",
1218 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
1219 | "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
1220 | "dev": true,
1221 | "optional": true
1222 | },
1223 | "@esbuild/linux-ppc64": {
1224 | "version": "0.18.20",
1225 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
1226 | "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
1227 | "dev": true,
1228 | "optional": true
1229 | },
1230 | "@esbuild/linux-riscv64": {
1231 | "version": "0.18.20",
1232 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
1233 | "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
1234 | "dev": true,
1235 | "optional": true
1236 | },
1237 | "@esbuild/linux-s390x": {
1238 | "version": "0.18.20",
1239 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
1240 | "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
1241 | "dev": true,
1242 | "optional": true
1243 | },
1244 | "@esbuild/linux-x64": {
1245 | "version": "0.18.20",
1246 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
1247 | "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
1248 | "dev": true,
1249 | "optional": true
1250 | },
1251 | "@esbuild/netbsd-x64": {
1252 | "version": "0.18.20",
1253 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
1254 | "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
1255 | "dev": true,
1256 | "optional": true
1257 | },
1258 | "@esbuild/openbsd-x64": {
1259 | "version": "0.18.20",
1260 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
1261 | "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
1262 | "dev": true,
1263 | "optional": true
1264 | },
1265 | "@esbuild/sunos-x64": {
1266 | "version": "0.18.20",
1267 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
1268 | "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
1269 | "dev": true,
1270 | "optional": true
1271 | },
1272 | "@esbuild/win32-arm64": {
1273 | "version": "0.18.20",
1274 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
1275 | "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
1276 | "dev": true,
1277 | "optional": true
1278 | },
1279 | "@esbuild/win32-ia32": {
1280 | "version": "0.18.20",
1281 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
1282 | "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
1283 | "dev": true,
1284 | "optional": true
1285 | },
1286 | "@esbuild/win32-x64": {
1287 | "version": "0.18.20",
1288 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
1289 | "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
1290 | "dev": true,
1291 | "optional": true
1292 | },
1293 | "@jridgewell/sourcemap-codec": {
1294 | "version": "1.4.15",
1295 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
1296 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
1297 | },
1298 | "@types/node": {
1299 | "version": "20.6.1",
1300 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.1.tgz",
1301 | "integrity": "sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g==",
1302 | "dev": true,
1303 | "optional": true,
1304 | "peer": true
1305 | },
1306 | "@vitejs/plugin-vue": {
1307 | "version": "4.3.4",
1308 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
1309 | "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
1310 | "dev": true,
1311 | "requires": {}
1312 | },
1313 | "@volar/language-core": {
1314 | "version": "1.10.1",
1315 | "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz",
1316 | "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==",
1317 | "dev": true,
1318 | "requires": {
1319 | "@volar/source-map": "1.10.1"
1320 | }
1321 | },
1322 | "@volar/source-map": {
1323 | "version": "1.10.1",
1324 | "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz",
1325 | "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==",
1326 | "dev": true,
1327 | "requires": {
1328 | "muggle-string": "^0.3.1"
1329 | }
1330 | },
1331 | "@volar/typescript": {
1332 | "version": "1.10.1",
1333 | "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz",
1334 | "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==",
1335 | "dev": true,
1336 | "requires": {
1337 | "@volar/language-core": "1.10.1"
1338 | }
1339 | },
1340 | "@vue/compiler-core": {
1341 | "version": "3.3.4",
1342 | "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz",
1343 | "integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==",
1344 | "requires": {
1345 | "@babel/parser": "^7.21.3",
1346 | "@vue/shared": "3.3.4",
1347 | "estree-walker": "^2.0.2",
1348 | "source-map-js": "^1.0.2"
1349 | }
1350 | },
1351 | "@vue/compiler-dom": {
1352 | "version": "3.3.4",
1353 | "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz",
1354 | "integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==",
1355 | "requires": {
1356 | "@vue/compiler-core": "3.3.4",
1357 | "@vue/shared": "3.3.4"
1358 | }
1359 | },
1360 | "@vue/compiler-sfc": {
1361 | "version": "3.3.4",
1362 | "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz",
1363 | "integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==",
1364 | "requires": {
1365 | "@babel/parser": "^7.20.15",
1366 | "@vue/compiler-core": "3.3.4",
1367 | "@vue/compiler-dom": "3.3.4",
1368 | "@vue/compiler-ssr": "3.3.4",
1369 | "@vue/reactivity-transform": "3.3.4",
1370 | "@vue/shared": "3.3.4",
1371 | "estree-walker": "^2.0.2",
1372 | "magic-string": "^0.30.0",
1373 | "postcss": "^8.1.10",
1374 | "source-map-js": "^1.0.2"
1375 | }
1376 | },
1377 | "@vue/compiler-ssr": {
1378 | "version": "3.3.4",
1379 | "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz",
1380 | "integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==",
1381 | "requires": {
1382 | "@vue/compiler-dom": "3.3.4",
1383 | "@vue/shared": "3.3.4"
1384 | }
1385 | },
1386 | "@vue/language-core": {
1387 | "version": "1.8.11",
1388 | "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.11.tgz",
1389 | "integrity": "sha512-+MZOBGqGwfld6hpo0DB47x8eNM0dNqk15ZdfOhj19CpvuYuOWCeVdOEGZunKDyo3QLkTn3kLOSysJzg7FDOQBA==",
1390 | "dev": true,
1391 | "requires": {
1392 | "@volar/language-core": "~1.10.0",
1393 | "@volar/source-map": "~1.10.0",
1394 | "@vue/compiler-dom": "^3.3.0",
1395 | "@vue/reactivity": "^3.3.0",
1396 | "@vue/shared": "^3.3.0",
1397 | "minimatch": "^9.0.0",
1398 | "muggle-string": "^0.3.1",
1399 | "vue-template-compiler": "^2.7.14"
1400 | }
1401 | },
1402 | "@vue/reactivity": {
1403 | "version": "3.3.4",
1404 | "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz",
1405 | "integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==",
1406 | "requires": {
1407 | "@vue/shared": "3.3.4"
1408 | }
1409 | },
1410 | "@vue/reactivity-transform": {
1411 | "version": "3.3.4",
1412 | "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz",
1413 | "integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==",
1414 | "requires": {
1415 | "@babel/parser": "^7.20.15",
1416 | "@vue/compiler-core": "3.3.4",
1417 | "@vue/shared": "3.3.4",
1418 | "estree-walker": "^2.0.2",
1419 | "magic-string": "^0.30.0"
1420 | }
1421 | },
1422 | "@vue/runtime-core": {
1423 | "version": "3.3.4",
1424 | "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz",
1425 | "integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==",
1426 | "requires": {
1427 | "@vue/reactivity": "3.3.4",
1428 | "@vue/shared": "3.3.4"
1429 | }
1430 | },
1431 | "@vue/runtime-dom": {
1432 | "version": "3.3.4",
1433 | "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz",
1434 | "integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==",
1435 | "requires": {
1436 | "@vue/runtime-core": "3.3.4",
1437 | "@vue/shared": "3.3.4",
1438 | "csstype": "^3.1.1"
1439 | }
1440 | },
1441 | "@vue/server-renderer": {
1442 | "version": "3.3.4",
1443 | "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz",
1444 | "integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==",
1445 | "requires": {
1446 | "@vue/compiler-ssr": "3.3.4",
1447 | "@vue/shared": "3.3.4"
1448 | }
1449 | },
1450 | "@vue/shared": {
1451 | "version": "3.3.4",
1452 | "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz",
1453 | "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
1454 | },
1455 | "@vue/typescript": {
1456 | "version": "1.8.11",
1457 | "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.11.tgz",
1458 | "integrity": "sha512-skUmMDiPUUtu1flPmf2YybF+PX8IzBtMioQOaNn6Ck/RhdrPJGj1AX/7s3Buf9G6ln+/KHR1XQuti/FFxw5XVA==",
1459 | "dev": true,
1460 | "requires": {
1461 | "@volar/typescript": "~1.10.0",
1462 | "@vue/language-core": "1.8.11"
1463 | }
1464 | },
1465 | "anymatch": {
1466 | "version": "3.1.3",
1467 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
1468 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
1469 | "dev": true,
1470 | "optional": true,
1471 | "peer": true,
1472 | "requires": {
1473 | "normalize-path": "^3.0.0",
1474 | "picomatch": "^2.0.4"
1475 | }
1476 | },
1477 | "balanced-match": {
1478 | "version": "1.0.2",
1479 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1480 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
1481 | "dev": true
1482 | },
1483 | "binary-extensions": {
1484 | "version": "2.2.0",
1485 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
1486 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
1487 | "dev": true,
1488 | "optional": true,
1489 | "peer": true
1490 | },
1491 | "brace-expansion": {
1492 | "version": "2.0.1",
1493 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
1494 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
1495 | "dev": true,
1496 | "requires": {
1497 | "balanced-match": "^1.0.0"
1498 | }
1499 | },
1500 | "braces": {
1501 | "version": "3.0.2",
1502 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
1503 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
1504 | "dev": true,
1505 | "optional": true,
1506 | "peer": true,
1507 | "requires": {
1508 | "fill-range": "^7.0.1"
1509 | }
1510 | },
1511 | "chokidar": {
1512 | "version": "3.5.3",
1513 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
1514 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
1515 | "dev": true,
1516 | "optional": true,
1517 | "peer": true,
1518 | "requires": {
1519 | "anymatch": "~3.1.2",
1520 | "braces": "~3.0.2",
1521 | "fsevents": "~2.3.2",
1522 | "glob-parent": "~5.1.2",
1523 | "is-binary-path": "~2.1.0",
1524 | "is-glob": "~4.0.1",
1525 | "normalize-path": "~3.0.0",
1526 | "readdirp": "~3.6.0"
1527 | }
1528 | },
1529 | "csstype": {
1530 | "version": "3.1.2",
1531 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
1532 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
1533 | },
1534 | "de-indent": {
1535 | "version": "1.0.2",
1536 | "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
1537 | "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
1538 | "dev": true
1539 | },
1540 | "esbuild": {
1541 | "version": "0.18.20",
1542 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
1543 | "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
1544 | "dev": true,
1545 | "requires": {
1546 | "@esbuild/android-arm": "0.18.20",
1547 | "@esbuild/android-arm64": "0.18.20",
1548 | "@esbuild/android-x64": "0.18.20",
1549 | "@esbuild/darwin-arm64": "0.18.20",
1550 | "@esbuild/darwin-x64": "0.18.20",
1551 | "@esbuild/freebsd-arm64": "0.18.20",
1552 | "@esbuild/freebsd-x64": "0.18.20",
1553 | "@esbuild/linux-arm": "0.18.20",
1554 | "@esbuild/linux-arm64": "0.18.20",
1555 | "@esbuild/linux-ia32": "0.18.20",
1556 | "@esbuild/linux-loong64": "0.18.20",
1557 | "@esbuild/linux-mips64el": "0.18.20",
1558 | "@esbuild/linux-ppc64": "0.18.20",
1559 | "@esbuild/linux-riscv64": "0.18.20",
1560 | "@esbuild/linux-s390x": "0.18.20",
1561 | "@esbuild/linux-x64": "0.18.20",
1562 | "@esbuild/netbsd-x64": "0.18.20",
1563 | "@esbuild/openbsd-x64": "0.18.20",
1564 | "@esbuild/sunos-x64": "0.18.20",
1565 | "@esbuild/win32-arm64": "0.18.20",
1566 | "@esbuild/win32-ia32": "0.18.20",
1567 | "@esbuild/win32-x64": "0.18.20"
1568 | }
1569 | },
1570 | "estree-walker": {
1571 | "version": "2.0.2",
1572 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
1573 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
1574 | },
1575 | "fill-range": {
1576 | "version": "7.0.1",
1577 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
1578 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
1579 | "dev": true,
1580 | "optional": true,
1581 | "peer": true,
1582 | "requires": {
1583 | "to-regex-range": "^5.0.1"
1584 | }
1585 | },
1586 | "fsevents": {
1587 | "version": "2.3.3",
1588 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1589 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1590 | "dev": true,
1591 | "optional": true
1592 | },
1593 | "glob-parent": {
1594 | "version": "5.1.2",
1595 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
1596 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
1597 | "dev": true,
1598 | "optional": true,
1599 | "peer": true,
1600 | "requires": {
1601 | "is-glob": "^4.0.1"
1602 | }
1603 | },
1604 | "he": {
1605 | "version": "1.2.0",
1606 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
1607 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
1608 | "dev": true
1609 | },
1610 | "immutable": {
1611 | "version": "4.3.4",
1612 | "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
1613 | "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
1614 | "dev": true,
1615 | "optional": true,
1616 | "peer": true
1617 | },
1618 | "is-binary-path": {
1619 | "version": "2.1.0",
1620 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1621 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1622 | "dev": true,
1623 | "optional": true,
1624 | "peer": true,
1625 | "requires": {
1626 | "binary-extensions": "^2.0.0"
1627 | }
1628 | },
1629 | "is-extglob": {
1630 | "version": "2.1.1",
1631 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1632 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
1633 | "dev": true,
1634 | "optional": true,
1635 | "peer": true
1636 | },
1637 | "is-glob": {
1638 | "version": "4.0.3",
1639 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
1640 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1641 | "dev": true,
1642 | "optional": true,
1643 | "peer": true,
1644 | "requires": {
1645 | "is-extglob": "^2.1.1"
1646 | }
1647 | },
1648 | "is-number": {
1649 | "version": "7.0.0",
1650 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1651 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1652 | "dev": true,
1653 | "optional": true,
1654 | "peer": true
1655 | },
1656 | "lru-cache": {
1657 | "version": "6.0.0",
1658 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
1659 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
1660 | "dev": true,
1661 | "requires": {
1662 | "yallist": "^4.0.0"
1663 | }
1664 | },
1665 | "magic-string": {
1666 | "version": "0.30.3",
1667 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz",
1668 | "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==",
1669 | "requires": {
1670 | "@jridgewell/sourcemap-codec": "^1.4.15"
1671 | }
1672 | },
1673 | "minimatch": {
1674 | "version": "9.0.3",
1675 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
1676 | "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
1677 | "dev": true,
1678 | "requires": {
1679 | "brace-expansion": "^2.0.1"
1680 | }
1681 | },
1682 | "muggle-string": {
1683 | "version": "0.3.1",
1684 | "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz",
1685 | "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==",
1686 | "dev": true
1687 | },
1688 | "nanoid": {
1689 | "version": "3.3.6",
1690 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
1691 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA=="
1692 | },
1693 | "normalize-path": {
1694 | "version": "3.0.0",
1695 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1696 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1697 | "dev": true,
1698 | "optional": true,
1699 | "peer": true
1700 | },
1701 | "picocolors": {
1702 | "version": "1.0.0",
1703 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
1704 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
1705 | },
1706 | "picomatch": {
1707 | "version": "2.3.1",
1708 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1709 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1710 | "dev": true,
1711 | "optional": true,
1712 | "peer": true
1713 | },
1714 | "postcss": {
1715 | "version": "8.4.29",
1716 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
1717 | "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
1718 | "requires": {
1719 | "nanoid": "^3.3.6",
1720 | "picocolors": "^1.0.0",
1721 | "source-map-js": "^1.0.2"
1722 | }
1723 | },
1724 | "readdirp": {
1725 | "version": "3.6.0",
1726 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1727 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1728 | "dev": true,
1729 | "optional": true,
1730 | "peer": true,
1731 | "requires": {
1732 | "picomatch": "^2.2.1"
1733 | }
1734 | },
1735 | "rollup": {
1736 | "version": "3.29.2",
1737 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.2.tgz",
1738 | "integrity": "sha512-CJouHoZ27v6siztc21eEQGo0kIcE5D1gVPA571ez0mMYb25LGYGKnVNXpEj5MGlepmDWGXNjDB5q7uNiPHC11A==",
1739 | "dev": true,
1740 | "requires": {
1741 | "fsevents": "~2.3.2"
1742 | }
1743 | },
1744 | "sass": {
1745 | "version": "1.67.0",
1746 | "resolved": "https://registry.npmjs.org/sass/-/sass-1.67.0.tgz",
1747 | "integrity": "sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A==",
1748 | "dev": true,
1749 | "optional": true,
1750 | "peer": true,
1751 | "requires": {
1752 | "chokidar": ">=3.0.0 <4.0.0",
1753 | "immutable": "^4.0.0",
1754 | "source-map-js": ">=0.6.2 <2.0.0"
1755 | }
1756 | },
1757 | "semver": {
1758 | "version": "7.5.4",
1759 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
1760 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
1761 | "dev": true,
1762 | "requires": {
1763 | "lru-cache": "^6.0.0"
1764 | }
1765 | },
1766 | "source-map-js": {
1767 | "version": "1.0.2",
1768 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
1769 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
1770 | },
1771 | "to-regex-range": {
1772 | "version": "5.0.1",
1773 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1774 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1775 | "dev": true,
1776 | "optional": true,
1777 | "peer": true,
1778 | "requires": {
1779 | "is-number": "^7.0.0"
1780 | }
1781 | },
1782 | "typescript": {
1783 | "version": "5.2.2",
1784 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
1785 | "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
1786 | "dev": true
1787 | },
1788 | "vite": {
1789 | "version": "4.4.9",
1790 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
1791 | "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
1792 | "dev": true,
1793 | "requires": {
1794 | "esbuild": "^0.18.10",
1795 | "fsevents": "~2.3.2",
1796 | "postcss": "^8.4.27",
1797 | "rollup": "^3.27.1"
1798 | }
1799 | },
1800 | "vue": {
1801 | "version": "3.3.4",
1802 | "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
1803 | "integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==",
1804 | "requires": {
1805 | "@vue/compiler-dom": "3.3.4",
1806 | "@vue/compiler-sfc": "3.3.4",
1807 | "@vue/runtime-dom": "3.3.4",
1808 | "@vue/server-renderer": "3.3.4",
1809 | "@vue/shared": "3.3.4"
1810 | }
1811 | },
1812 | "vue-template-compiler": {
1813 | "version": "2.7.14",
1814 | "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
1815 | "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
1816 | "dev": true,
1817 | "requires": {
1818 | "de-indent": "^1.0.2",
1819 | "he": "^1.2.0"
1820 | }
1821 | },
1822 | "vue-tsc": {
1823 | "version": "1.8.11",
1824 | "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.11.tgz",
1825 | "integrity": "sha512-BzfiMdPqDHBlysx4g26NkfVHSQwGD/lTRausmxN9sFyjXz34OWfsbkh0YsVkX84Hu65In1fFlxHiG39Tr4Vojg==",
1826 | "dev": true,
1827 | "requires": {
1828 | "@vue/language-core": "1.8.11",
1829 | "@vue/typescript": "1.8.11",
1830 | "semver": "^7.3.8"
1831 | }
1832 | },
1833 | "yallist": {
1834 | "version": "4.0.0",
1835 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1836 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
1837 | "dev": true
1838 | }
1839 | }
1840 | }
1841 |
--------------------------------------------------------------------------------