├── public
├── favicon.ico
├── og_image.png
├── Bitstream-Vera-Sans-Mono-400.woff
└── Bitstream-Vera-Sans-Mono-400.woff2
├── .prettierrc
├── pages
├── _app.js
└── index.js
├── index.html
├── src
├── utils
│ ├── withUuids.js
│ ├── hooks
│ │ └── useWindowSize.js
│ ├── infoStringHelpers.js
│ └── constants.js
├── main.jsx
├── styles
│ ├── editor.module.scss
│ ├── paste_box.module.scss
│ ├── color_picker.module.scss
│ ├── example_shapes.module.scss
│ ├── viewer.module.scss
│ ├── button_row.module.scss
│ ├── home.module.scss
│ └── command.module.scss
├── components
│ ├── color_picker.jsx
│ ├── learn_more.jsx
│ ├── editor.jsx
│ ├── example_shapes.jsx
│ ├── head.jsx
│ ├── paste_box.jsx
│ ├── button_row.jsx
│ ├── viewer.jsx
│ └── command.jsx
├── index.css
├── App.jsx
└── models
│ └── command_model.js
├── vite.config.js
├── .gitignore
├── README.md
├── package.json
├── LICENSE
├── .github
└── workflows
│ └── deploy.yml
└── yarn.lock
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rfauver/svg_path_editor/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/og_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rfauver/svg_path_editor/HEAD/public/og_image.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "jsxSingleQuote": true,
4 | "arrowParens": "avoid",
5 | "trailingComma": "es5"
6 | }
7 |
--------------------------------------------------------------------------------
/public/Bitstream-Vera-Sans-Mono-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rfauver/svg_path_editor/HEAD/public/Bitstream-Vera-Sans-Mono-400.woff
--------------------------------------------------------------------------------
/public/Bitstream-Vera-Sans-Mono-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rfauver/svg_path_editor/HEAD/public/Bitstream-Vera-Sans-Mono-400.woff2
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 |
3 | function MyApp({ Component, pageProps }) {
4 | return
5 | }
6 |
7 | export default MyApp
8 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/utils/withUuids.js:
--------------------------------------------------------------------------------
1 | import { v4 as uuidv4 } from 'uuid';
2 |
3 | export default function withUuids(strings) {
4 | return strings.map(str => ({ raw: str, uuid: uuidv4() }));
5 | }
6 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import App from './App.jsx'
5 |
6 | createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/src/styles/editor.module.scss:
--------------------------------------------------------------------------------
1 | .component {
2 | height: 100%;
3 | display: flex;
4 | flex-direction: column;
5 | font-family: var(--font-mono);
6 | font-size: 14px;
7 | line-height: 18px;
8 | min-width: 504px;
9 |
10 | @media (max-width: 1060px) {
11 | min-width: 0;
12 | }
13 | }
14 |
15 | .indented {
16 | margin-left: 20px;
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /out/
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | pnpm-debug.log*
9 | lerna-debug.log*
10 |
11 | .env
12 |
13 | node_modules
14 | dist
15 | dist-ssr
16 | *.local
17 |
18 | # Editor directories and files
19 | .vscode/*
20 | !.vscode/extensions.json
21 | .idea
22 | .DS_Store
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw?
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SVG Path Editor
2 |
3 | SVG Path Editor is an interactive tool to edit an SVG by editing the path commands that describe its shape. See it running at [svg-path.com](https://svg-path.com)
4 |
5 |
6 |
7 | ## License
8 | [MIT](https://github.com/rfauver/svg_path_editor/blob/main/LICENSE)
9 |
--------------------------------------------------------------------------------
/src/styles/paste_box.module.scss:
--------------------------------------------------------------------------------
1 | .component {
2 | width: 537px;
3 | height: 68px;
4 | margin-right: auto;
5 | padding: 8px 12px;
6 | border-radius: 6px;
7 | border: none;
8 | outline: none;
9 | font-size: 14px;
10 | line-height: 18px;
11 | color: var(--text-color);
12 | font-family: var(--font-mono);
13 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);
14 | background-color: var(--panel-color);
15 |
16 | @media (max-width: 620px) {
17 | width: 100%;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/hooks/useWindowSize.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | export function useWindowSize() {
4 | const [windowSize, setWindowSize] = useState({
5 | width: 0,
6 | height: 0,
7 | });
8 |
9 | const handleSize = () => {
10 | setWindowSize({
11 | width: window.innerWidth,
12 | height: window.innerHeight,
13 | });
14 | };
15 |
16 | useEffect(() => {
17 | handleSize();
18 | window.addEventListener('resize', handleSize);
19 | return () => window.removeEventListener('resize', handleSize);
20 | }, []);
21 |
22 | return windowSize;
23 | }
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svg_path_editor",
3 | "version": "0.1.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "classnames": "^2.5.1",
13 | "react": "^19.2.0",
14 | "react-dom": "^19.2.0",
15 | "react-helmet": "^6.1.0",
16 | "sass": "^1.70.0",
17 | "uuid": "^9.0.1"
18 | },
19 | "devDependencies": {
20 | "@vitejs/plugin-react": "^5.1.1",
21 | "globals": "^16.5.0",
22 | "prettier": "3.2.4",
23 | "vite": "^7.2.4"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/styles/color_picker.module.scss:
--------------------------------------------------------------------------------
1 | .fillColor {
2 | color: var(--text-color);
3 | padding: 4px;
4 | border-radius: 4px;
5 | width: 67px;
6 | border: none;
7 | background: unset;
8 | outline: none;
9 | font-family: inherit;
10 | font-size: inherit;
11 | &:hover,
12 | &:focus {
13 | box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.08);
14 | background-color: var(--background-color);
15 | }
16 | }
17 |
18 | .colorPickerWrapper {
19 | border-radius: 50%;
20 | border: 1px solid rgba(0, 0, 0, 0.2);
21 | position: relative;
22 | bottom: 1px;
23 | }
24 |
25 | .colorPicker {
26 | opacity: 0;
27 | width: 17px;
28 | cursor: pointer;
29 | }
30 |
--------------------------------------------------------------------------------
/src/styles/example_shapes.module.scss:
--------------------------------------------------------------------------------
1 | .component {
2 | display: flex;
3 | width: 100%;
4 | margin: 14px 0 10px;
5 |
6 | @media (max-width: 620px) {
7 | overflow: auto;
8 | padding-bottom: 4px;
9 | }
10 | }
11 |
12 | .shape {
13 | min-width: 60px;
14 | height: 60px;
15 | background-color: var(--panel-color);
16 | border-radius: 10px;
17 | border: none;
18 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);
19 | padding: 10px;
20 | margin-right: 16px;
21 | cursor: pointer;
22 |
23 | &:hover {
24 | background-color: var(--button-hover-color);
25 | }
26 |
27 | &:active {
28 | transform: scale(0.95);
29 | box-shadow: 1px 1px 4px rgb(0 0 0 / 25%);
30 | }
31 | }
32 |
33 | .paste {
34 | color: var(--text-color);
35 | font-size: 16px;
36 | padding: 10px 20px;
37 | min-width: 80px;
38 | }
39 |
--------------------------------------------------------------------------------
/src/styles/viewer.module.scss:
--------------------------------------------------------------------------------
1 | .component {
2 | display: flex;
3 | margin: 14px 0 0 6px;
4 |
5 | @media (max-width: 620px) {
6 | margin: 6px 0 0 4px;
7 | }
8 |
9 | svg path {
10 | transition: d 300ms;
11 | }
12 | svg circle {
13 | transition:
14 | cx 300ms,
15 | cy 300ms;
16 | }
17 | }
18 |
19 | .yCoords {
20 | display: flex;
21 | flex-direction: column;
22 | justify-content: space-between;
23 | text-align: right;
24 | margin: 20px 4px 1px 4px;
25 | }
26 |
27 | .rightWrapper {
28 | width: 100%;
29 | }
30 |
31 | .xCoords {
32 | display: flex;
33 | justify-content: space-between;
34 | margin-bottom: 2px;
35 | }
36 |
37 | .coord {
38 | @media (max-width: 620px) {
39 | font-size: 14px;
40 | }
41 | }
42 |
43 | .view {
44 | border-top: 1px dashed var(--axis-color);
45 | border-left: 1px dashed var(--axis-color);
46 | }
47 |
--------------------------------------------------------------------------------
/src/styles/button_row.module.scss:
--------------------------------------------------------------------------------
1 | .component {
2 | height: 60px;
3 | margin-top: auto;
4 | display: flex;
5 | gap: 10px;
6 |
7 | button {
8 | cursor: pointer;
9 | flex: 1;
10 | font-size: 16px;
11 | padding: 4px 6px;
12 | border-radius: 10px;
13 | border: none;
14 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);
15 | background-color: var(--panel-color);
16 | color: var(--text-color);
17 | svg {
18 | height: 16px;
19 | }
20 | &:hover {
21 | background-color: var(--button-hover-color);
22 | }
23 | &:active {
24 | transform: scale(0.95);
25 | }
26 | }
27 |
28 | .convert {
29 | min-width: 152px;
30 | }
31 |
32 | @media (max-width: 1060px) {
33 | height: 52px;
34 |
35 | button {
36 | font-size: 14px;
37 | }
38 |
39 | .convert {
40 | min-width: 136px;
41 | }
42 | }
43 |
44 | @media (max-width: 500px) {
45 | .convert {
46 | min-width: 0;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/color_picker.jsx:
--------------------------------------------------------------------------------
1 | import styles from '../styles/color_picker.module.scss';
2 |
3 | export default function ColorPicker({ fillColor, setFillColor }) {
4 | const onFillColorChange = e => {
5 | let input = e.target.value.trimStart();
6 | if (input[0] !== '#') {
7 | input = '#' + input;
8 | }
9 | setFillColor(input);
10 | };
11 | const onColorPickerChange = e => {
12 | setFillColor(e.target.value);
13 | };
14 |
15 | return (
16 | <>
17 |
25 |
29 |
36 |
37 | >
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Ryan Fauver
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 |
--------------------------------------------------------------------------------
/src/components/learn_more.jsx:
--------------------------------------------------------------------------------
1 | export default function LearnMore() {
2 | return (
3 | <>
4 | Learn More
5 |
6 |
10 | MDN Docs on the d attribute
11 |
12 |
13 |
14 |
18 | The SVG `path` Syntax: An Illustrated Guide from CSS-Tricks
19 |
20 |
21 |
22 |
23 | Demystifyingish SVG paths from HTTP 203
24 |
25 |
26 |
27 |
28 | SVG Path Visualizer
29 |
30 |
31 |
32 |
33 | Source code on Github
34 |
35 |
36 | >
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/editor.jsx:
--------------------------------------------------------------------------------
1 | import ColorPicker from '../components/color_picker';
2 | import Command from '../components/command';
3 | import { firstLine } from '../utils/constants';
4 |
5 | import styles from '../styles/editor.module.scss';
6 |
7 | export default function Editor({
8 | commands,
9 | fillColor,
10 | setFillColor,
11 | setCursorPosition,
12 | updateInstructions,
13 | addCommand,
14 | removeCommand,
15 | setHoveredIndex,
16 | setActiveIndex,
17 | maxCoord,
18 | }) {
19 | return (
20 |
21 |
{firstLine(maxCoord)}
22 |
41 |
{''}
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/example_shapes.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import PasteBox from '../components/paste_box';
3 | import { SHAPES } from '../utils/constants';
4 | import withUuids from '../utils/withUuids';
5 | import classnames from 'classnames';
6 |
7 | import styles from '../styles/example_shapes.module.scss';
8 |
9 | export default function ExampleShapes({ fillColor, setInstructions }) {
10 | const [showPasteBox, setShowPasteBox] = useState(false);
11 |
12 | return (
13 | <>
14 |
15 | {Object.entries(SHAPES).map(([name, shapeInstructions]) => {
16 | return (
17 |
setInstructions(withUuids(shapeInstructions))}
21 | >
22 |
23 |
24 |
25 |
26 | );
27 | })}
28 |
setShowPasteBox(!showPasteBox)}
31 | >
32 | Paste
33 |
34 |
35 | {showPasteBox && }
36 | >
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | # Simple workflow for deploying static content to GitHub Pages
2 | name: Deploy static content to Pages
3 |
4 | on:
5 | # Runs on pushes targeting the default branch
6 | push:
7 | branches: ['main']
8 |
9 | # Allows you to run this workflow manually from the Actions tab
10 | workflow_dispatch:
11 |
12 | # Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages
13 | permissions:
14 | contents: read
15 | pages: write
16 | id-token: write
17 |
18 | # Allow one concurrent deployment
19 | concurrency:
20 | group: 'pages'
21 | cancel-in-progress: true
22 |
23 | jobs:
24 | # Single deploy job since we're just deploying
25 | deploy:
26 | environment:
27 | name: github-pages
28 | url: ${{ steps.deployment.outputs.page_url }}
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: Checkout
32 | uses: actions/checkout@v5
33 | - name: Set up Node
34 | uses: actions/setup-node@v6
35 | with:
36 | node-version: lts/*
37 | cache: 'yarn'
38 | - name: Install dependencies
39 | run: yarn install --frozen-lockfile
40 | - name: Build
41 | run: yarn build
42 | env:
43 | VITE_FATHOM_DOMAIN: ${{ vars.VITE_FATHOM_DOMAIN }}
44 | VITE_FATHOM_ID: ${{ vars.VITE_FATHOM_ID }}
45 | - name: Setup Pages
46 | uses: actions/configure-pages@v5
47 | - name: Upload artifact
48 | uses: actions/upload-pages-artifact@v4
49 | with:
50 | # Upload dist folder
51 | path: './dist'
52 | - name: Deploy to GitHub Pages
53 | id: deployment
54 | uses: actions/deploy-pages@v4
55 |
--------------------------------------------------------------------------------
/src/components/head.jsx:
--------------------------------------------------------------------------------
1 | import { Helmet } from 'react-helmet';
2 |
3 | export default function Head({ instructions, svgText }) {
4 | return (
5 | <>
6 |
7 | SVG Path Editor
8 |
12 |
16 |
17 |
22 |
23 |
24 |
25 | {/* Fathom - simple website analytics */}
26 | {import.meta.env.PROD && (
27 |
40 | )}
41 | {/* / Fathom */}
42 | >
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --font-sans: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
3 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
4 | --font-mono: 'Bitstream Vera Sans Mono', monospace;
5 | --background-color: white;
6 | --panel-color: #f2f2f2;
7 | --input-color: var(--background-color);
8 | --text-color: #222;
9 | --link-color: #0070f3;
10 | --toggle-disabled-color: #ccc;
11 | --info-color: #777;
12 | --button-hover-color: #e2e3e5;
13 | --axis-color: #70769e;
14 | --input-shadow: 1px 1px 4px rgba(0, 0, 0, 0.08);
15 | --highlight-input-shadow: 1px 1px 4px rgba(0, 0, 0, 0.25);
16 | --annotation-shadow: 3px 3px 4px 1px rgba(0, 0, 0, 0.16);
17 | }
18 |
19 | @media (prefers-color-scheme: dark) {
20 | :root {
21 | --background-color: #2a303a;
22 | --panel-color: #37393e;
23 | --input-color: #252424;
24 | --text-color: #eee;
25 | --link-color: #5da4f6;
26 | --toggle-disabled-color: var(--input-color);
27 | --info-color: #ccc;
28 | --button-hover-color: #444851;
29 | --axis-color: #888;
30 | --input-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
31 | --highlight-input-shadow: var(--input-shadow);
32 | --annotation-shadow: 4px 4px 8px 1px rgba(0, 0, 0, 0.4);
33 | }
34 | }
35 |
36 | html,
37 | body {
38 | padding: 0;
39 | margin: 0;
40 | font-family: var(--font-sans);
41 | background-color: var(--background-color);
42 | color: var(--text-color);
43 | }
44 |
45 | a {
46 | color: var(--link-color);
47 | text-decoration: none;
48 | }
49 |
50 | * {
51 | box-sizing: border-box;
52 | }
53 |
54 | @font-face {
55 | font-family: 'Bitstream Vera Sans Mono';
56 | src:
57 | url('../Bitstream-Vera-Sans-Mono-400.woff2') format('woff2'),
58 | url('../Bitstream-Vera-Sans-Mono-400.woff') format('woff');
59 | font-display: swap;
60 | }
61 |
--------------------------------------------------------------------------------
/src/styles/home.module.scss:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | max-width: 1500px;
4 | padding: 0 24px;
5 | margin: 0 auto;
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 |
10 | @media (max-width: 1060px) {
11 | max-width: 790px;
12 | padding: 0 14px;
13 | }
14 | }
15 |
16 | .heading {
17 | font-size: 90px;
18 | width: 100%;
19 | margin: 20px 0 0;
20 |
21 | @media (max-width: 1060px) {
22 | font-size: 60px;
23 | }
24 | }
25 |
26 | .intro {
27 | width: 100%;
28 | p {
29 | max-width: 625px;
30 | line-height: 22px;
31 | }
32 | }
33 |
34 | .code {
35 | font-family: 'Bitstream Vera Sans Mono', monospace;
36 | font-size: 14px;
37 | background: #e9e9e9;
38 | padding: 2px 6px;
39 | border-radius: 4px;
40 | }
41 |
42 | .main {
43 | display: grid;
44 | grid-template-columns: repeat(2, 1fr);
45 | column-gap: 30px;
46 | width: 100%;
47 | margin: 10px 0;
48 | @media (max-width: 1060px) {
49 | grid-template-columns: 1fr;
50 | }
51 | }
52 |
53 | .sectionWrapper {
54 | display: flex;
55 | flex-direction: column;
56 | gap: 10px;
57 |
58 | @media (max-width: 1060px) {
59 | max-width: 750px;
60 | margin-bottom: 10px;
61 | flex-direction: column-reverse;
62 | }
63 | }
64 |
65 | .section {
66 | height: 100%;
67 | background-color: var(--panel-color);
68 | border-radius: 20px;
69 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);
70 | padding: 20px 24px;
71 | @media (max-width: 1060px) {
72 | max-width: 750px;
73 | }
74 | @media (max-width: 620px) {
75 | padding: 14px;
76 | }
77 | }
78 |
79 | .viewer {
80 | position: sticky;
81 | top: 24px;
82 | height: fit-content;
83 | padding: 0 24px 20px 0;
84 | @media (max-width: 620px) {
85 | padding: 0 14px 10px 0;
86 | }
87 | }
88 |
89 | .learnMore {
90 | width: 100%;
91 | margin-bottom: 80px;
92 | }
93 |
--------------------------------------------------------------------------------
/src/utils/infoStringHelpers.js:
--------------------------------------------------------------------------------
1 | export const currentPositionString = prev =>
2 | prev ? ` (${prev[0]},${prev[1]})` : '';
3 |
4 | const optPlus = coord => (typeof coord === 'number' && coord < 0 ? '' : '+');
5 |
6 | export const relativeToString = (v, prev, names = ['x', 'y']) => {
7 | if (v.every(coord => typeof coord === 'number') && prev) {
8 | return `${prev[0] + v[0]},${prev[1] + v[1]} (${prev[0]}${optPlus(v[0])}${
9 | v[0]
10 | }, ${prev[1]}${optPlus(v[1])}${v[1]})`;
11 | } else if (prev) {
12 | return `${prev[0]}${optPlus(v[0])}${v[0]}, ${prev[1]}${optPlus(v[1])}${
13 | v[1]
14 | }`;
15 | } else {
16 | return `(current ${names[0]}+${v[0]}, current ${names[1]}+${v[1]})`;
17 | }
18 | };
19 |
20 | export const relativeHorizontalToString = (v, prev) => {
21 | if (v.every(coord => typeof coord === 'number') && prev) {
22 | return `${prev[0] + v[0]},${prev[1]} (${prev[0]}${optPlus(v[0])}${v[0]}, ${
23 | prev[1]
24 | })`;
25 | } else if (prev) {
26 | return `${prev[0]}${optPlus(v[0])}${v[0]}, ${prev[1]}`;
27 | } else {
28 | return `(current x${optPlus(v[0])}${v[0]}, current y)`;
29 | }
30 | };
31 |
32 | export const relativeVerticalToString = (v, prev) => {
33 | if (v.every(coord => typeof coord === 'number') && prev) {
34 | return `${prev[0]},${prev[1] + v[0]} (${prev[0]},${optPlus(v[0])}${v[0]})`;
35 | } else if (prev) {
36 | return `${prev[0]}, ${prev[1]}${optPlus(v[0])}${v[0]}`;
37 | } else {
38 | return `(current x, current y${optPlus(v[0])}${v[0]})`;
39 | }
40 | };
41 |
42 | export const arcFlagString = largeArc => {
43 | if (typeof largeArc === 'number' && [0, 1].includes(largeArc)) {
44 | return `using the ${largeArc === 0 ? 'smaller' : 'larger'} of the two arcs`;
45 | } else {
46 | `with large-arc-flag (0 or 1) deciding between the smaller or larger arc`;
47 | }
48 | };
49 |
50 | export const sweepFlagString = sweep => {
51 | if (typeof sweep === 'number' && [0, 1].includes(sweep)) {
52 | return `using the ${sweep === 0 ? 'counterclockwise' : 'clockwise'} arc`;
53 | } else {
54 | `with sweep-flag (0 or 1) deciding between the counterclockwise or clockwise arc`;
55 | }
56 | };
57 |
--------------------------------------------------------------------------------
/src/components/paste_box.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react';
2 | import { COMMANDS, DIGIT } from '../utils/constants';
3 | import withUuids from '../utils/withUuids';
4 |
5 | import styles from '../styles/paste_box.module.scss';
6 |
7 | export default function PasteBox({ setInstructions }) {
8 | const pasteBoxRef = useRef(null);
9 |
10 | useEffect(() => {
11 | if (pasteBoxRef.current) {
12 | pasteBoxRef.current.focus();
13 | }
14 | }, [pasteBoxRef]);
15 |
16 | const handlePasteChange = e => {
17 | const input = e.target.value;
18 | if (input.trim() === '') return;
19 |
20 | const commandChars = Object.keys(COMMANDS).join('');
21 | const parts = (
22 | (input.replace(/-/g, ' -') + ' ').match(
23 | new RegExp(`[${commandChars}][^${commandChars}]+`, 'gi')
24 | ) || []
25 | )
26 | .map(part => {
27 | const letter = part[0];
28 |
29 | if (letter.toUpperCase() === 'Z') {
30 | return ['Z'];
31 | }
32 | const rest = part.slice(1).trim();
33 | const coords = (rest.match(new RegExp(DIGIT, 'g')) || []).map(coord =>
34 | coord.replace(/^\./, '0.').replace('-.', '-0.')
35 | );
36 | if (coords.some(coord => coord.trim() === '')) {
37 | return null;
38 | }
39 | const groupLength = COMMANDS[letter.toUpperCase()].partNames.length;
40 | const grouped = coords.reduce((groups, coord, i) => {
41 | i % groupLength !== 0
42 | ? groups[groups.length - 1].push(coord)
43 | : groups.push([coord]);
44 | return groups;
45 | }, []);
46 | if (grouped.some(group => group.length !== groupLength)) {
47 | return null;
48 | }
49 | return grouped.map((group, i) => {
50 | let implicitLetter = letter;
51 | if (i !== 0 && letter.toUpperCase() === 'M') {
52 | implicitLetter = { m: 'l', M: 'L' }[letter];
53 | }
54 | return `${implicitLetter}${group.join(',')}`;
55 | });
56 | })
57 | .flat(1);
58 |
59 | if (parts.length > 0 && parts.every(Boolean)) {
60 | setInstructions(withUuids(parts));
61 | }
62 | };
63 |
64 | return (
65 |
71 | );
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/button_row.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { useWindowSize } from '../utils/hooks/useWindowSize';
3 |
4 | import styles from '../styles/button_row.module.scss';
5 |
6 | export default function ButtonRow({ svgText, commands, setInstructions }) {
7 | const windowSize = useWindowSize();
8 | const [copyText, setCopyText] = useState('Copy');
9 | const [copyTimeout, setCopyTimeout] = useState(null);
10 | const onCopyClicked = e => {
11 | if (e && e.button !== 0) return;
12 | navigator.clipboard.writeText(svgText);
13 | if (copyTimeout) clearTimeout(copyTimeout);
14 | setCopyText('Copied!');
15 | setCopyTimeout(setTimeout(() => setCopyText('Copy'), 2000));
16 | };
17 | const onDownloadClicked = e => {
18 | if (e && e.button !== 0) return;
19 | const dataURI = `data:image/svg+xml,${encodeURIComponent(svgText)}`;
20 | const link = document.createElement('a');
21 | link.href = dataURI;
22 | link.download = 'path_editor.svg';
23 | document.body.appendChild(link);
24 | link.click();
25 | link.parentNode.removeChild(link);
26 | };
27 | const onAbsoluteClicked = e => {
28 | if (e && e.button !== 0) return;
29 | setInstructions(
30 | commands.map(command => ({
31 | raw: command.absoluteInstruction(),
32 | uuid: command.uuid,
33 | }))
34 | );
35 | };
36 | const onRelativeClicked = e => {
37 | if (e && e.button !== 0) return;
38 | setInstructions(
39 | commands.map(command => ({
40 | raw: command.relativeInstruction(),
41 | uuid: command.uuid,
42 | }))
43 | );
44 | };
45 |
46 | const onPress = clickHandler => e => {
47 | if ([' ', 'Enter'].includes(e.key)) {
48 | clickHandler();
49 | }
50 | };
51 |
52 | const convertText = str =>
53 | windowSize.width > 500 ? `Convert to ${str}` : str;
54 |
55 | return (
56 |
57 |
63 | {convertText('Absolute')}
64 |
65 |
71 | {convertText('Relative')}
72 |
73 |
74 |
79 | {copyText}
80 |
81 |
86 | Download
87 |
88 |
89 | );
90 | }
91 |
--------------------------------------------------------------------------------
/src/components/viewer.jsx:
--------------------------------------------------------------------------------
1 | import classnames from 'classnames';
2 |
3 | import styles from '../styles/viewer.module.scss';
4 |
5 | export default function Viewer({
6 | commands,
7 | fillColor,
8 | maxCoord,
9 | hoveredIndex,
10 | activeIndex,
11 | }) {
12 | return (
13 |
14 |
15 |
0
16 |
{maxCoord}
17 |
18 |
19 |
20 |
0
21 |
{maxCoord}
22 |
23 |
24 |
29 | c.instruction).join(' ')}
32 | />
33 | {commands
34 | .map((command, i) =>
35 | [hoveredIndex, activeIndex].includes(i)
36 | ? getPathHighlight(command, i, maxCoord)
37 | : null
38 | )
39 | .filter(Boolean)}
40 |
41 |
42 |
43 | );
44 | }
45 |
46 | function getPathHighlight(command, index, maxCoord) {
47 | if (command.isA('M')) {
48 | const [x, y] = command.endPoint() || [0, 0];
49 | return (
50 |
58 | );
59 | }
60 | let pathString = '';
61 | if (command.previousEndPoint) {
62 | if (command.isA('Z')) {
63 | pathString = `M${command.previousEndPoint} L${
64 | command.previousMEndPoint || [0, 0]
65 | }`;
66 | } else {
67 | pathString = `M${command.previousEndPoint} ${command.instruction}`;
68 | }
69 | }
70 |
71 | if (
72 | ((command.isA('S') &&
73 | ['S', 'C'].includes(command.previousLetter?.toUpperCase())) ||
74 | (command.isA('T') &&
75 | ['T', 'Q'].includes(command.previousLetter?.toUpperCase()))) &&
76 | command.previousControlPoint &&
77 | command.previousEndPoint
78 | ) {
79 | const diffX = command.previousEndPoint[0] - command.previousControlPoint[0];
80 | const diffY = command.previousEndPoint[1] - command.previousControlPoint[1];
81 | const newControlPoint = [
82 | command.previousEndPoint[0] + diffX,
83 | command.previousEndPoint[1] + diffY,
84 | ];
85 | const curveType = { S: 'C', T: 'Q' }[command.letter.toUpperCase()];
86 |
87 | pathString = `M${command.previousEndPoint} ${curveType}${newControlPoint} ${command.absoluteInstruction().slice(1)}`;
88 | }
89 | return (
90 |
99 | );
100 | }
101 |
--------------------------------------------------------------------------------
/src/components/command.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { COMMANDS } from '../utils/constants';
3 | import classnames from 'classnames';
4 |
5 | import styles from '../styles/command.module.scss';
6 |
7 | export default function Command({
8 | command,
9 | index,
10 | setCursorPosition,
11 | updateInstructions,
12 | addCommand,
13 | removeCommand,
14 | setHoveredIndex,
15 | setActiveIndex,
16 | }) {
17 | const onInputChange = e =>
18 | updateInstructions(index, e.target.value.trimStart());
19 | const onKeyUp = e => {
20 | if (e.key === 'Enter') {
21 | addCommand(index);
22 | }
23 | onCursorChange(e);
24 | };
25 | const onCursorChange = e => setCursorPosition(e.target.selectionStart);
26 | const addCommandPressed = e => {
27 | if ([' ', 'Enter'].includes(e.key)) {
28 | addCommand(index);
29 | }
30 | };
31 | const removeCommandPressed = e => {
32 | if ([' ', 'Enter', 'Backspace', 'Delete'].includes(e.key)) {
33 | removeCommand(index);
34 | }
35 | };
36 | const addCommandClicked = () => addCommand(index);
37 | const removeCommandClicked = () => removeCommand(index);
38 |
39 | const onFocus = () => setActiveIndex(index);
40 | const onBlur = () => setActiveIndex(null);
41 | const onInputFocus = e => {
42 | onCursorChange(e);
43 | onFocus();
44 | };
45 |
46 | const [infoOpen, setInfoOpen] = useState(false);
47 | return (
48 | setHoveredIndex(index)}
53 | onMouseLeave={() => setHoveredIndex(null)}
54 | >
55 |
56 |
66 | {command.properties?.partNames && (
67 |
68 | (
69 | {command.properties.partNames.map((partName, i) => (
70 |
76 | {partName}
77 | {i < command.properties.partNames.length - 1 ? ', ' : ''}
78 |
79 | ))}
80 | )
81 |
82 | )}
83 |
84 |
updateInstructions(index, e.target.value)}
88 | value={command.letter?.toUpperCase()}
89 | onFocus={onFocus}
90 | onBlur={onBlur}
91 | >
92 | {Object.entries(COMMANDS).map(([commandLetter, command]) => (
93 |
94 | {command.name}
95 |
96 | ))}
97 |
98 |
99 | Rel
100 |
106 | updateInstructions(
107 | index,
108 | e.target.checked
109 | ? command.instruction.toLowerCase()
110 | : command.instruction.toUpperCase()
111 | )
112 | }
113 | onFocus={onFocus}
114 | onBlur={onBlur}
115 | />
116 |
117 |
118 |
119 |
127 |
135 |
136 |
setInfoOpen(!infoOpen)}>
137 | i
138 |
139 |
140 |
141 | {infoOpen && (
142 |
143 |
{command.infoString() || ''}
144 |
149 |
150 | )}
151 |
152 | );
153 | }
154 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import Editor from './components/editor';
3 | import ExampleShapes from './components/example_shapes';
4 | import Head from './components/head';
5 | import LearnMore from './components/learn_more';
6 | import Viewer from './components/viewer';
7 | import ButtonRow from './components/button_row';
8 | import CommandModel from './models/command_model';
9 | import { SHAPES, firstLine } from './utils/constants';
10 | import withUuids from './utils/withUuids';
11 | import classnames from 'classnames';
12 |
13 | import styles from './styles/home.module.scss';
14 |
15 | const toRaw = instructions => instructions.map(instruction => instruction.raw);
16 |
17 | export default function App() {
18 | const [instructions, setInstructions] = useState(
19 | withUuids(SHAPES.QUARTER_NOTE)
20 | );
21 | const [fillColor, setFillColor] = useState('#0070f3');
22 | const [cursorPosition, setCursorPosition] = useState(null);
23 | const [indexToFocus, setIndexToFocus] = useState(null);
24 | useEffect(() => {
25 | if (indexToFocus === null) return;
26 |
27 | const command = document.querySelectorAll('.instruction')[indexToFocus];
28 | command?.focus();
29 |
30 | setIndexToFocus(null);
31 | }, [indexToFocus]);
32 | const [hoveredIndex, setHoveredIndex] = useState(null);
33 | const [activeIndex, setActiveIndex] = useState(null);
34 |
35 | const updateInstructions = (i, instruction) => {
36 | const newInstructions = [...instructions];
37 | newInstructions[i] = { ...newInstructions[i], raw: instruction };
38 | setInstructions(newInstructions);
39 | };
40 | const addCommand = index => {
41 | setInstructions([
42 | ...instructions.slice(0, index + 1),
43 | { raw: 'L', uuid: crypto.randomUUID() },
44 | ...instructions.slice(index + 1),
45 | ]);
46 | setIndexToFocus(index + 1);
47 | };
48 | const removeCommand = index => {
49 | if (instructions.length <= 1) {
50 | setInstructions(['M']);
51 | setIndexToFocus(0);
52 | return;
53 | }
54 | setInstructions([
55 | ...instructions.slice(0, index),
56 | ...instructions.slice(index + 1),
57 | ]);
58 | };
59 |
60 | let previousEndPoint, previousMEndPoint, previousControlPoint, previousLetter;
61 | const commands = instructions.map(instruction => {
62 | const command = new CommandModel(
63 | instruction.uuid,
64 | instruction.raw,
65 | cursorPosition,
66 | previousEndPoint,
67 | previousMEndPoint,
68 | previousControlPoint,
69 | previousLetter
70 | );
71 | previousEndPoint = command.endPoint();
72 | if (command.isA('M')) previousMEndPoint = previousEndPoint;
73 |
74 | const match = command.instruction.match(
75 | command.properties?.parts?.[command.properties.parts.length - 1]
76 | );
77 | if (match) {
78 | command.setPartValues(match.slice(1));
79 | }
80 | previousControlPoint = command.lastControlPoint();
81 | previousLetter = command.letter;
82 |
83 | return command;
84 | });
85 | const endPoints = commands.map(command => command.endPoint()).filter(Boolean);
86 | const maxCoord = Math.ceil(Math.max(...endPoints.flat(), 100));
87 |
88 | const svgText = [
89 | firstLine(maxCoord),
90 | ` ',
93 | '',
94 | ].join('\n');
95 |
96 | return (
97 |
98 |
99 |
100 | SVG Path Editor
101 |
102 |
103 | SVG paths are specified as a list of commands. Each command describes
104 | a step along the path. This editor allows you to create an SVG by
105 | editing the individual commands that describe its shape.
106 |
107 |
108 |
109 |
110 |
111 |
112 |
124 |
125 |
130 |
131 |
132 |
133 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | );
147 | }
148 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import Editor from '../components/editor';
3 | import ExampleShapes from '../components/example_shapes';
4 | import Head from '../components/head';
5 | import LearnMore from '../components/learn_more';
6 | import Viewer from '../components/viewer';
7 | import ButtonRow from '../components/button_row';
8 | import CommandModel from '../models/command_model';
9 | import { SHAPES, firstLine } from '../utils/constants';
10 | import withUuids from '../utils/withUuids';
11 | import classnames from 'classnames';
12 |
13 | import styles from '../styles/home.module.scss';
14 |
15 | const toRaw = instructions => instructions.map(instruction => instruction.raw);
16 |
17 | export default function Home() {
18 | const [instructions, setInstructions] = useState(
19 | withUuids(SHAPES.QUARTER_NOTE)
20 | );
21 | const [fillColor, setFillColor] = useState('#0070f3');
22 | const [cursorPosition, setCursorPosition] = useState(null);
23 | const [indexToFocus, setIndexToFocus] = useState(null);
24 | useEffect(() => {
25 | if (indexToFocus === null) return;
26 |
27 | const command = document.querySelectorAll('.instruction')[indexToFocus];
28 | command?.focus();
29 |
30 | setIndexToFocus(null);
31 | }, [indexToFocus]);
32 | const [hoveredIndex, setHoveredIndex] = useState(null);
33 | const [activeIndex, setActiveIndex] = useState(null);
34 |
35 | const updateInstructions = (i, instruction) => {
36 | const newInstructions = [...instructions];
37 | newInstructions[i] = { ...newInstructions[i], raw: instruction };
38 | setInstructions(newInstructions);
39 | };
40 | const addCommand = index => {
41 | setInstructions([
42 | ...instructions.slice(0, index + 1),
43 | { raw: 'L', uuid: crypto.randomUUID() },
44 | ...instructions.slice(index + 1),
45 | ]);
46 | setIndexToFocus(index + 1);
47 | };
48 | const removeCommand = index => {
49 | if (instructions.length <= 1) {
50 | setInstructions(['M']);
51 | setIndexToFocus(0);
52 | return;
53 | }
54 | setInstructions([
55 | ...instructions.slice(0, index),
56 | ...instructions.slice(index + 1),
57 | ]);
58 | };
59 |
60 | let previousEndPoint, previousMEndPoint, previousControlPoint, previousLetter;
61 | const commands = instructions.map(instruction => {
62 | const command = new CommandModel(
63 | instruction.uuid,
64 | instruction.raw,
65 | cursorPosition,
66 | previousEndPoint,
67 | previousMEndPoint,
68 | previousControlPoint,
69 | previousLetter
70 | );
71 | previousEndPoint = command.endPoint();
72 | if (command.isA('M')) previousMEndPoint = previousEndPoint;
73 |
74 | const match = command.instruction.match(
75 | command.properties?.parts?.[command.properties.parts.length - 1]
76 | );
77 | if (match) {
78 | command.setPartValues(match.slice(1));
79 | }
80 | previousControlPoint = command.lastControlPoint();
81 | previousLetter = command.letter;
82 |
83 | return command;
84 | });
85 | const endPoints = commands.map(command => command.endPoint()).filter(Boolean);
86 | const maxCoord = Math.ceil(Math.max(...endPoints.flat(), 100));
87 |
88 | const svgText = [
89 | firstLine(maxCoord),
90 | ` ',
93 | '',
94 | ].join('\n');
95 |
96 | return (
97 |
98 |
99 |
100 | SVG Path Editor
101 |
102 |
103 | SVG paths are specified as a list of commands. Each command describes
104 | a step along the path. This editor allows you to create an SVG by
105 | editing the individual commands that describe its shape.
106 |
107 |
108 |
109 |
110 |
111 |
112 |
124 |
125 |
130 |
131 |
132 |
133 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | );
147 | }
148 |
--------------------------------------------------------------------------------
/src/styles/command.module.scss:
--------------------------------------------------------------------------------
1 | .component {
2 | position: relative;
3 |
4 | margin-right: -12px;
5 | padding-right: 12px;
6 | margin-left: 24px;
7 | padding-left: 12px;
8 | border-radius: 6px;
9 |
10 | @media (max-width: 620px) {
11 | margin-right: -8px;
12 | padding-right: 8px;
13 | }
14 |
15 | &:hover,
16 | &:focus-within {
17 | .dropdown,
18 | .relativeToggle,
19 | .addButton,
20 | .removeButton,
21 | .info {
22 | display: block;
23 | }
24 | .instruction {
25 | box-shadow: var(--input-shadow);
26 | background-color: var(--input-color);
27 | }
28 | }
29 | }
30 |
31 | .highlighted {
32 | background-color: var(--background-color);
33 | padding-top: 2px;
34 | margin-bottom: 2px;
35 |
36 | &:hover,
37 | &:focus-within {
38 | .instruction,
39 | .dropdown,
40 | .toggleSwitch,
41 | .button {
42 | box-shadow: var(--highlight-input-shadow);
43 | }
44 | }
45 | }
46 |
47 | .commandLine {
48 | display: flex;
49 | align-items: center;
50 | column-gap: 10px;
51 | row-gap: 4px;
52 |
53 | @media (max-width: 620px) {
54 | flex-wrap: wrap;
55 | }
56 | }
57 |
58 | .instruction {
59 | flex: 1 1 auto;
60 | font-family: var(--font-mono);
61 | color: var(--text-color);
62 | font-size: 16px;
63 | line-height: 18px;
64 | padding: 4px;
65 | margin: 2px 0 2px 0px;
66 | border-radius: 4px;
67 | border: none;
68 | background: unset;
69 | outline: none;
70 | }
71 |
72 | .annotation {
73 | display: none;
74 | position: absolute;
75 | padding: 4px;
76 | top: -32px;
77 | background-color: var(--input-color);
78 | color: #999;
79 | font-size: 14px;
80 | border-radius: 4px;
81 | box-shadow: var(--annotation-shadow);
82 | z-index: 100;
83 |
84 | &:before {
85 | content: '';
86 | display: block;
87 | position: absolute;
88 | margin-left: -6px;
89 | border-top: 6px solid var(--input-color);
90 | border-left: 6px solid transparent;
91 | border-right: 6px solid transparent;
92 | height: 0;
93 | width: 0;
94 | left: 50%;
95 | top: 26px;
96 | }
97 | }
98 |
99 | .instruction:focus + .annotation {
100 | display: block;
101 | }
102 |
103 | .partName {
104 | }
105 |
106 | .partNameActive {
107 | color: var(--text-color);
108 | }
109 |
110 | .controls {
111 | display: flex;
112 | align-items: center;
113 | gap: 10px;
114 | @media (max-width: 565px) {
115 | width: 100%;
116 | }
117 | }
118 |
119 | .dropdown {
120 | width: 100px;
121 | background-color: var(--input-color);
122 | color: var(--text-color);
123 | border: none;
124 | padding: 4px;
125 | box-shadow: var(--input-shadow);
126 | border-radius: 4px;
127 |
128 | @media (max-width: 565px) {
129 | width: 100%;
130 | }
131 | }
132 |
133 | .dropdown,
134 | .relativeToggle,
135 | .addButton,
136 | .removeButton,
137 | .info {
138 | display: none;
139 | }
140 |
141 | .relativeToggle {
142 | position: relative;
143 | height: 18px;
144 | min-width: 64px;
145 | font-family: var(--font-sans);
146 | }
147 |
148 | .toggleCheckbox {
149 | opacity: 0;
150 | position: absolute;
151 | left: -10000px;
152 | pointer-events: none;
153 |
154 | &:checked {
155 | & + .toggleSwitch {
156 | background-color: #66bb6a;
157 | &:before {
158 | transform: translateX(16px);
159 | }
160 | }
161 | }
162 |
163 | &:focus-visible + .toggleSwitch {
164 | outline: -webkit-focus-ring-color auto 1px;
165 | }
166 | }
167 |
168 | .toggleSwitch {
169 | background-color: var(--toggle-disabled-color);
170 | position: absolute;
171 | cursor: pointer;
172 | border-radius: 4px;
173 | bottom: 0;
174 | left: 28px;
175 | right: 0;
176 | top: 0;
177 | transition: background-color 0.4s;
178 | box-shadow: var(--input-shadow);
179 |
180 | &:before {
181 | background-color: white;
182 | border-radius: 4px;
183 | bottom: 2px;
184 | content: '';
185 | height: 14px;
186 | left: 2px;
187 | position: absolute;
188 | transition: transform 0.4s;
189 | width: 16px;
190 | box-shadow: var(--input-shadow);
191 | }
192 | }
193 |
194 | .buttons {
195 | display: flex;
196 | }
197 |
198 | .button {
199 | font-family: Arial;
200 | color: white;
201 | border: none;
202 | border-radius: 4px;
203 | box-shadow: var(--input-shadow);
204 | width: 18px;
205 | height: 18px;
206 | line-height: 18px;
207 | cursor: pointer;
208 | padding: 0;
209 | font-size: 20px;
210 | position: relative;
211 | &:before {
212 | position: absolute;
213 | }
214 | }
215 |
216 | .addButton {
217 | background-color: #66bb6a;
218 | &:before {
219 | content: '+';
220 | bottom: 0;
221 | left: 3px;
222 | }
223 | }
224 |
225 | .removeButton {
226 | margin-left: 4px;
227 | background-color: #d64747;
228 | &:before {
229 | content: '-';
230 | bottom: 2px;
231 | left: 6px;
232 | }
233 | }
234 |
235 | .info {
236 | border: 1px solid var(--info-color);
237 | color: var(--info-color);
238 | border-radius: 100%;
239 | min-width: 16px;
240 | height: 16px;
241 | text-align: center;
242 | font-size: 12px;
243 | line-height: 15px;
244 | cursor: pointer;
245 | user-select: none;
246 | }
247 |
248 | .infoBox {
249 | padding: 4px 3px 8px;
250 | background-color: var(--background-color);
251 | font-size: 14px;
252 | font-family: var(--font-sans);
253 | border-radius: 4px;
254 | z-index: 100;
255 | }
256 |
257 | .infoString {
258 | margin-bottom: 4px;
259 | }
260 |
--------------------------------------------------------------------------------
/src/models/command_model.js:
--------------------------------------------------------------------------------
1 | import { COMMANDS, DIGIT, SEPARATOR } from '../utils/constants';
2 |
3 | export default class CommandModel {
4 | constructor(
5 | uuid,
6 | instruction,
7 | cursorPosition,
8 | previousEndPoint,
9 | previousMEndPoint,
10 | previousControlPoint,
11 | previousLetter
12 | ) {
13 | this.uuid = uuid;
14 | this.instruction = instruction;
15 | this.letter = instruction[0];
16 | this.cursorPosition = cursorPosition;
17 | this.previousEndPoint = previousEndPoint;
18 | this.previousMEndPoint = previousMEndPoint;
19 | this.previousControlPoint = previousControlPoint;
20 | this.previousLetter = previousLetter;
21 | this.properties = COMMANDS[this.letter?.toUpperCase()];
22 | }
23 |
24 | isRelative = () => this.letter && this.letter.toLowerCase() === this.letter;
25 |
26 | isA = commandLetter =>
27 | this.letter && this.letter.toUpperCase() === commandLetter.toUpperCase();
28 |
29 | activePartIndex = () => {
30 | return (this.properties?.parts || []).findIndex(part =>
31 | (this.cursorPosition
32 | ? this.instruction.substring(0, this.cursorPosition)
33 | : ''
34 | ).match(part)
35 | );
36 | };
37 |
38 | setPartValues = values => {
39 | this.partValues = values;
40 | };
41 |
42 | infoString = () => {
43 | if (!this.properties) return '';
44 |
45 | const values = this.properties.partNames?.map((partName, i) =>
46 | this.partValues?.[i] ? parseFloat(this.partValues[i]) : partName
47 | );
48 |
49 | return this.properties.info(
50 | !this.isRelative(),
51 | values,
52 | this.previousEndPoint?.map(v => parseFloat(v)),
53 | this.previousMEndPoint?.map(v => parseFloat(v))
54 | );
55 | };
56 |
57 | endPoint = () => {
58 | if (this.isA('Z')) {
59 | return this.previousMEndPoint;
60 | }
61 |
62 | const relativeOffset =
63 | this.isRelative() && this.previousEndPoint
64 | ? this.previousEndPoint
65 | : [0, 0];
66 |
67 | let match;
68 | if (this.isA('H')) {
69 | match = this.instruction.match(new RegExp(`${DIGIT.source}$`));
70 | if (!match) return null;
71 | return [
72 | parseFloat(match[1]) + relativeOffset[0],
73 | this.previousEndPoint?.[1] || 0,
74 | ];
75 | }
76 | if (this.isA('V')) {
77 | match = this.instruction.match(new RegExp(`${DIGIT.source}$`));
78 | if (!match) return null;
79 | return [
80 | this.previousEndPoint?.[0] || 0,
81 | parseFloat(match[1]) + relativeOffset[1],
82 | ];
83 | }
84 |
85 | match = this.instruction.match(
86 | new RegExp(`${DIGIT.source}${SEPARATOR.source}${DIGIT.source}$`)
87 | );
88 | if (!match) return null;
89 | return match
90 | .slice(1, 3)
91 | .map((point, i) => parseFloat(point) + relativeOffset[i]);
92 | };
93 |
94 | lastControlPoint = () => {
95 | if (
96 | ['M', 'L', 'H', 'V', 'A', 'Z'].some(letter => this.isA(letter)) ||
97 | !this.partValues
98 | ) {
99 | return null;
100 | }
101 |
102 | const relativeOffset =
103 | this.isRelative() && this.previousEndPoint
104 | ? this.previousEndPoint
105 | : [0, 0];
106 |
107 | if (this.isA('C')) {
108 | return [
109 | parseFloat(this.partValues[2]) + relativeOffset[0],
110 | parseFloat(this.partValues[3]) + relativeOffset[1],
111 | ];
112 | }
113 | if (this.isA('Q') || this.isA('S')) {
114 | return [
115 | parseFloat(this.partValues[0]) + relativeOffset[0],
116 | parseFloat(this.partValues[1]) + relativeOffset[1],
117 | ];
118 | }
119 | if (this.isA('T') && this.previousEndPoint) {
120 | if (
121 | this.previousControlPoint &&
122 | !['S', 'C'].includes(this.previousLetter?.toUpperCase())
123 | ) {
124 | const diffX = this.previousEndPoint[0] - this.previousControlPoint[0];
125 | const diffY = this.previousEndPoint[1] - this.previousControlPoint[1];
126 | return [
127 | this.previousEndPoint[0] + diffX,
128 | this.previousEndPoint[1] + diffY,
129 | ];
130 | } else {
131 | return this.previousEndPoint;
132 | }
133 | }
134 | };
135 |
136 | #toRelativeX = x =>
137 | +(parseFloat(x) - (this.previousEndPoint?.[0] || 0)).toFixed(2);
138 | #toRelativeY = y =>
139 | +(parseFloat(y) - (this.previousEndPoint?.[1] || 0)).toFixed(2);
140 | #toAbsoluteX = x =>
141 | +(parseFloat(x) + (this.previousEndPoint?.[0] || 0)).toFixed(2);
142 | #toAbsoluteY = y =>
143 | +(parseFloat(y) + (this.previousEndPoint?.[1] || 0)).toFixed(2);
144 |
145 | relativeInstruction = () => {
146 | if (this.isRelative()) return this.instruction;
147 |
148 | let newInstruction = `${this.letter.toLowerCase()}${this.instruction.substring(
149 | 1
150 | )}`;
151 |
152 | switch (this.letter) {
153 | case 'H':
154 | return newInstruction.replace(DIGIT, x => this.#toRelativeX(x));
155 | case 'V':
156 | return newInstruction.replace(DIGIT, y => this.#toRelativeY(y));
157 | case 'A':
158 | return newInstruction
159 | .trim()
160 | .replace(
161 | new RegExp(`${DIGIT.source}(${SEPARATOR.source})${DIGIT.source}$`),
162 | (_, x, separator, y) =>
163 | `${this.#toRelativeX(x)}${separator}${this.#toRelativeY(y)}`
164 | );
165 | default:
166 | return newInstruction.replace(
167 | new RegExp(
168 | `${DIGIT.source}(${SEPARATOR.source})${DIGIT.source}`,
169 | 'g'
170 | ),
171 | (_, x, separator, y) =>
172 | `${this.#toRelativeX(x)}${separator}${this.#toRelativeY(y)}`
173 | );
174 | }
175 | };
176 |
177 | absoluteInstruction = () => {
178 | if (!this.letter || !this.isRelative()) return this.instruction;
179 |
180 | let newInstruction = `${this.letter.toUpperCase()}${this.instruction.substring(
181 | 1
182 | )}`;
183 |
184 | switch (this.letter) {
185 | case 'h':
186 | return newInstruction.replace(DIGIT, x => this.#toAbsoluteX(x));
187 | case 'v':
188 | return newInstruction.replace(DIGIT, y => this.#toAbsoluteY(y));
189 | case 'a':
190 | return newInstruction
191 | .trim()
192 | .replace(
193 | new RegExp(`${DIGIT.source}(${SEPARATOR.source})${DIGIT.source}$`),
194 | (_, x, separator, y) =>
195 | `${this.#toAbsoluteX(x)}${separator}${this.#toAbsoluteY(y)}`
196 | );
197 | default:
198 | return newInstruction.replace(
199 | new RegExp(
200 | `${DIGIT.source}(${SEPARATOR.source})${DIGIT.source}`,
201 | 'g'
202 | ),
203 | (_, x, separator, y) =>
204 | `${this.#toAbsoluteX(x)}${separator}${this.#toAbsoluteY(y)}`
205 | );
206 | }
207 | };
208 | }
209 |
--------------------------------------------------------------------------------
/src/utils/constants.js:
--------------------------------------------------------------------------------
1 | import {
2 | currentPositionString,
3 | relativeToString,
4 | relativeHorizontalToString,
5 | relativeVerticalToString,
6 | arcFlagString,
7 | sweepFlagString,
8 | } from './infoStringHelpers';
9 |
10 | export const DIGIT = /((?:\+|-)?\d*\.?\d+)/;
11 | const LAST_DIGIT = /((?:\+|-)?\d*\.?\d*)/;
12 | export const SEPARATOR = /\s*[ ,]\s*/;
13 |
14 | function buildParts(letter, length) {
15 | return [...new Array(length)].map(
16 | (_, i) =>
17 | new RegExp(
18 | `^${letter}\\s*${[...new Array(i + 1)]
19 | .map((_, j) => (i === j ? LAST_DIGIT.source : DIGIT.source))
20 | .join(SEPARATOR.source)}$`,
21 | 'i'
22 | )
23 | );
24 | }
25 |
26 | export const COMMANDS = {
27 | M: {
28 | name: 'Move',
29 | parts: buildParts('M', 2),
30 | partNames: ['x', 'y'],
31 | info: (absolute, v, prev) =>
32 | `Move the pen from its current position${currentPositionString(
33 | prev
34 | )} to ${absolute ? `${v[0]},${v[1]}` : relativeToString(v, prev)}`,
35 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataMovetoCommands',
36 | },
37 | L: {
38 | name: 'Line',
39 | parts: buildParts('L', 2),
40 | partNames: ['x', 'y'],
41 | info: (absolute, v, prev) =>
42 | `Draw a straight line from the current position${currentPositionString(
43 | prev
44 | )} to ${absolute ? `${v[0]},${v[1]}` : relativeToString(v, prev)}`,
45 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataLinetoCommands',
46 | },
47 | H: {
48 | name: 'Horizontal Line',
49 | parts: buildParts('H', 1),
50 | partNames: ['x'],
51 | info: (absolute, v, prev) =>
52 | `Draw a horizontal line from the current position${currentPositionString(
53 | prev
54 | )} to ${
55 | absolute ? `${v[0]},${prev[1]}` : relativeHorizontalToString(v, prev)
56 | }`,
57 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataLinetoCommands',
58 | },
59 | V: {
60 | name: 'Vertical Line',
61 | parts: buildParts('V', 1),
62 | partNames: ['y'],
63 | info: (absolute, v, prev) =>
64 | `Draw a vertical line from the current position${currentPositionString(
65 | prev
66 | )} to ${
67 | absolute ? `${v[0]},${prev[1]}` : relativeVerticalToString(v, prev)
68 | }`,
69 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataLinetoCommands',
70 | },
71 | C: {
72 | name: 'Cubic Bézier Curve',
73 | parts: buildParts('C', 6),
74 | partNames: ['x1', 'y1', 'x2', 'y2', 'x', 'y'],
75 | info: (absolute, v, prev) =>
76 | `Draw a bézier curve from the current position${currentPositionString(
77 | prev
78 | )} to ${
79 | absolute ? `${v[4]},${v[5]}` : relativeToString(v.slice(4, 6), prev)
80 | } with ${
81 | absolute ? `${v[0]},${v[1]}` : relativeToString(v, prev, ['x1', 'y1'])
82 | } as the start control point and ${
83 | absolute
84 | ? `${v[2]},${v[3]}`
85 | : relativeToString(v.slice(2, 4), prev, ['x2', 'y2'])
86 | } as the end control point`,
87 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataCubicBezierCommands',
88 | },
89 | S: {
90 | name: 'Shortcut Cubic Curve',
91 | parts: buildParts('S', 4),
92 | partNames: ['x2', 'y2', 'x', 'y'],
93 | info: (absolute, v, prev) =>
94 | `Draw a bézier curve from the current position${currentPositionString(
95 | prev
96 | )} to ${
97 | absolute ? `${v[2]},${v[3]}` : relativeToString(v.slice(2, 4), prev)
98 | } with ${
99 | absolute ? `${v[0]},${v[1]}` : relativeToString(v, prev, ['x2', 'y2'])
100 | } as the end control point and a reflection of the previous curve command's end control point as the start control point (if it exists)`,
101 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataCubicBezierCommands',
102 | },
103 | Q: {
104 | name: 'Quadratic Bézier Curve',
105 | parts: buildParts('Q', 4),
106 | partNames: ['x1', 'y1', 'x', 'y'],
107 | info: (absolute, v, prev) =>
108 | `Draw a bézier curve from the current position${currentPositionString(
109 | prev
110 | )} to ${
111 | absolute ? `${v[2]},${v[3]}` : relativeToString(v.slice(2, 4), prev)
112 | } with a single control point at ${
113 | absolute ? `${v[0]},${v[1]}` : relativeToString(v, prev, ['x1', 'y1'])
114 | }`,
115 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataQuadraticBezierCommands',
116 | },
117 | T: {
118 | name: 'Shortcut Quadratic Curve',
119 | parts: buildParts('T', 2),
120 | partNames: ['x', 'y'],
121 | info: (absolute, v, prev) =>
122 | `Draw a bézier curve from the current position${currentPositionString(
123 | prev
124 | )} to ${
125 | absolute ? `${v[0]},${v[1]}` : relativeToString(v, prev)
126 | }, using a reflection of the previous curve command's control point as the single control point`,
127 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataQuadraticBezierCommands',
128 | },
129 | A: {
130 | name: 'Elliptical Arc Curve',
131 | parts: buildParts('A', 7),
132 | partNames: ['rx', 'ry', 'angle', 'large-arc-flag', 'sweep-flag', 'x', 'y'],
133 | info: (absolute, v, prev) =>
134 | `Draw an arc from the current position${currentPositionString(prev)} to ${
135 | absolute ? `${v[5]},${v[6]}` : relativeToString(v.slice(5, 7), prev)
136 | }, with ${v[0]} and ${v[1]} as the radii of the ellipse, ${
137 | v[2]
138 | } as the angle of rotation of the ellipse, ${arcFlagString(
139 | v[3]
140 | )}, and ${sweepFlagString(v[4])}`,
141 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataEllipticalArcCommands',
142 | },
143 | Z: {
144 | name: 'Close Path',
145 | info: (absolute, v, prev, prevM) =>
146 | `Draw a straight line from the current position${currentPositionString(
147 | prev
148 | )} back to the start of the path${currentPositionString(prevM)}`,
149 | link: 'https://svgwg.org/svg2-draft/paths.html#PathDataClosePathCommand',
150 | },
151 | };
152 |
153 | export const firstLine = maxCoord =>
154 | ``;
155 |
156 | export const SHAPES = {
157 | QUARTER_NOTE: [
158 | 'M45,0',
159 | 'L68,30',
160 | 'C50,50,55,50,70,75',
161 | 'L67,78',
162 | 'C50,70,40,80,53,97',
163 | 'L50,100',
164 | 'C30,75,30,60,57,68',
165 | 'L35,40',
166 | 'Q60,25,38,0',
167 | 'Z',
168 | ],
169 | STAR: [
170 | 'M50,0',
171 | 'L58.82,37.86',
172 | 'L97.55,34.55',
173 | 'L64.27,54.64',
174 | 'L78.39,90.45',
175 | 'L50,65',
176 | 'L21.61,90.45',
177 | 'L35.73,54.64',
178 | 'L2.45,34.55',
179 | 'L41.18,37.86',
180 | 'Z',
181 | ],
182 | SMILEY: [
183 | 'm50,0',
184 | 'a50,50,0,0,0,0,100',
185 | 'a-50,-50,0,0,0,0,-100',
186 | 'z',
187 | 'm-8,32',
188 | 'a8,8,0,0,1,-16,0',
189 | 'a8,8,0,0,1,16,0',
190 | 'z',
191 | 'm32,0',
192 | 'a8,8,0,0,1,-16,0',
193 | 'a8,8,0,0,1,16,0',
194 | 'z',
195 | 'm-54,30',
196 | 'h60',
197 | 'c-5,26,-55,26,-60,0',
198 | 'z',
199 | ],
200 | PLUS: [
201 | 'm35,0',
202 | 'v35',
203 | 'h-35',
204 | 'v30',
205 | 'h35',
206 | 'v35',
207 | 'h30',
208 | 'v-35',
209 | 'h35',
210 | 'v-30',
211 | 'h-35',
212 | 'v-35',
213 | 'z',
214 | ],
215 | HEART: [
216 | 'M50,25',
217 | 'C35,0,-14,25,20,60',
218 | 'L50,90',
219 | 'L80,60',
220 | 'C114,20,65,0,50,25',
221 | ],
222 | SPARKLES: [
223 | 'M70,16',
224 | 'c0,20,-10,30,-20,30',
225 | 'c10,0,20,10,20,30',
226 | 'c0,-20,10,-30,20,-30',
227 | 'c-10,0,-20,-10,-20,-30',
228 | 'z',
229 | 'M30,0',
230 | 'c0,20,-10,30,-20,30',
231 | 'c10,0,20,10,20,30',
232 | 'c0,-20,10,-30,20,-30',
233 | 'c-10,0,-20,-10,-20,-30',
234 | 'z',
235 | 'M43,42',
236 | 'c0,20,-10,30,-20,30',
237 | 'c10,0,20,10,20,30',
238 | 'c0,-20,10,-30,20,-30',
239 | 'c-10,0,-20,-10,-20,-30',
240 | 'z',
241 | ],
242 | };
243 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@babel/code-frame@^7.27.1":
6 | version "7.27.1"
7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
8 | integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
9 | dependencies:
10 | "@babel/helper-validator-identifier" "^7.27.1"
11 | js-tokens "^4.0.0"
12 | picocolors "^1.1.1"
13 |
14 | "@babel/compat-data@^7.27.2":
15 | version "7.28.5"
16 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f"
17 | integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==
18 |
19 | "@babel/core@^7.28.5":
20 | version "7.28.5"
21 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e"
22 | integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==
23 | dependencies:
24 | "@babel/code-frame" "^7.27.1"
25 | "@babel/generator" "^7.28.5"
26 | "@babel/helper-compilation-targets" "^7.27.2"
27 | "@babel/helper-module-transforms" "^7.28.3"
28 | "@babel/helpers" "^7.28.4"
29 | "@babel/parser" "^7.28.5"
30 | "@babel/template" "^7.27.2"
31 | "@babel/traverse" "^7.28.5"
32 | "@babel/types" "^7.28.5"
33 | "@jridgewell/remapping" "^2.3.5"
34 | convert-source-map "^2.0.0"
35 | debug "^4.1.0"
36 | gensync "^1.0.0-beta.2"
37 | json5 "^2.2.3"
38 | semver "^6.3.1"
39 |
40 | "@babel/generator@^7.28.5":
41 | version "7.28.5"
42 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298"
43 | integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==
44 | dependencies:
45 | "@babel/parser" "^7.28.5"
46 | "@babel/types" "^7.28.5"
47 | "@jridgewell/gen-mapping" "^0.3.12"
48 | "@jridgewell/trace-mapping" "^0.3.28"
49 | jsesc "^3.0.2"
50 |
51 | "@babel/helper-compilation-targets@^7.27.2":
52 | version "7.27.2"
53 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d"
54 | integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==
55 | dependencies:
56 | "@babel/compat-data" "^7.27.2"
57 | "@babel/helper-validator-option" "^7.27.1"
58 | browserslist "^4.24.0"
59 | lru-cache "^5.1.1"
60 | semver "^6.3.1"
61 |
62 | "@babel/helper-globals@^7.28.0":
63 | version "7.28.0"
64 | resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
65 | integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
66 |
67 | "@babel/helper-module-imports@^7.27.1":
68 | version "7.27.1"
69 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204"
70 | integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==
71 | dependencies:
72 | "@babel/traverse" "^7.27.1"
73 | "@babel/types" "^7.27.1"
74 |
75 | "@babel/helper-module-transforms@^7.28.3":
76 | version "7.28.3"
77 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6"
78 | integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==
79 | dependencies:
80 | "@babel/helper-module-imports" "^7.27.1"
81 | "@babel/helper-validator-identifier" "^7.27.1"
82 | "@babel/traverse" "^7.28.3"
83 |
84 | "@babel/helper-plugin-utils@^7.27.1":
85 | version "7.27.1"
86 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c"
87 | integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==
88 |
89 | "@babel/helper-string-parser@^7.27.1":
90 | version "7.27.1"
91 | resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
92 | integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
93 |
94 | "@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5":
95 | version "7.28.5"
96 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
97 | integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
98 |
99 | "@babel/helper-validator-option@^7.27.1":
100 | version "7.27.1"
101 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f"
102 | integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==
103 |
104 | "@babel/helpers@^7.28.4":
105 | version "7.28.4"
106 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827"
107 | integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==
108 | dependencies:
109 | "@babel/template" "^7.27.2"
110 | "@babel/types" "^7.28.4"
111 |
112 | "@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5":
113 | version "7.28.5"
114 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08"
115 | integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==
116 | dependencies:
117 | "@babel/types" "^7.28.5"
118 |
119 | "@babel/plugin-transform-react-jsx-self@^7.27.1":
120 | version "7.27.1"
121 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz#af678d8506acf52c577cac73ff7fe6615c85fc92"
122 | integrity sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==
123 | dependencies:
124 | "@babel/helper-plugin-utils" "^7.27.1"
125 |
126 | "@babel/plugin-transform-react-jsx-source@^7.27.1":
127 | version "7.27.1"
128 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz#dcfe2c24094bb757bf73960374e7c55e434f19f0"
129 | integrity sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==
130 | dependencies:
131 | "@babel/helper-plugin-utils" "^7.27.1"
132 |
133 | "@babel/template@^7.27.2":
134 | version "7.27.2"
135 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
136 | integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
137 | dependencies:
138 | "@babel/code-frame" "^7.27.1"
139 | "@babel/parser" "^7.27.2"
140 | "@babel/types" "^7.27.1"
141 |
142 | "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.5":
143 | version "7.28.5"
144 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b"
145 | integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==
146 | dependencies:
147 | "@babel/code-frame" "^7.27.1"
148 | "@babel/generator" "^7.28.5"
149 | "@babel/helper-globals" "^7.28.0"
150 | "@babel/parser" "^7.28.5"
151 | "@babel/template" "^7.27.2"
152 | "@babel/types" "^7.28.5"
153 | debug "^4.3.1"
154 |
155 | "@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5":
156 | version "7.28.5"
157 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b"
158 | integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==
159 | dependencies:
160 | "@babel/helper-string-parser" "^7.27.1"
161 | "@babel/helper-validator-identifier" "^7.28.5"
162 |
163 | "@esbuild/aix-ppc64@0.25.12":
164 | version "0.25.12"
165 | resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c"
166 | integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==
167 |
168 | "@esbuild/android-arm64@0.25.12":
169 | version "0.25.12"
170 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752"
171 | integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==
172 |
173 | "@esbuild/android-arm@0.25.12":
174 | version "0.25.12"
175 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a"
176 | integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==
177 |
178 | "@esbuild/android-x64@0.25.12":
179 | version "0.25.12"
180 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16"
181 | integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==
182 |
183 | "@esbuild/darwin-arm64@0.25.12":
184 | version "0.25.12"
185 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd"
186 | integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==
187 |
188 | "@esbuild/darwin-x64@0.25.12":
189 | version "0.25.12"
190 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e"
191 | integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==
192 |
193 | "@esbuild/freebsd-arm64@0.25.12":
194 | version "0.25.12"
195 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe"
196 | integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==
197 |
198 | "@esbuild/freebsd-x64@0.25.12":
199 | version "0.25.12"
200 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3"
201 | integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==
202 |
203 | "@esbuild/linux-arm64@0.25.12":
204 | version "0.25.12"
205 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977"
206 | integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==
207 |
208 | "@esbuild/linux-arm@0.25.12":
209 | version "0.25.12"
210 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9"
211 | integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==
212 |
213 | "@esbuild/linux-ia32@0.25.12":
214 | version "0.25.12"
215 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0"
216 | integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==
217 |
218 | "@esbuild/linux-loong64@0.25.12":
219 | version "0.25.12"
220 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0"
221 | integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==
222 |
223 | "@esbuild/linux-mips64el@0.25.12":
224 | version "0.25.12"
225 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd"
226 | integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==
227 |
228 | "@esbuild/linux-ppc64@0.25.12":
229 | version "0.25.12"
230 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869"
231 | integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==
232 |
233 | "@esbuild/linux-riscv64@0.25.12":
234 | version "0.25.12"
235 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6"
236 | integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==
237 |
238 | "@esbuild/linux-s390x@0.25.12":
239 | version "0.25.12"
240 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663"
241 | integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==
242 |
243 | "@esbuild/linux-x64@0.25.12":
244 | version "0.25.12"
245 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306"
246 | integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==
247 |
248 | "@esbuild/netbsd-arm64@0.25.12":
249 | version "0.25.12"
250 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4"
251 | integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==
252 |
253 | "@esbuild/netbsd-x64@0.25.12":
254 | version "0.25.12"
255 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076"
256 | integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==
257 |
258 | "@esbuild/openbsd-arm64@0.25.12":
259 | version "0.25.12"
260 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd"
261 | integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==
262 |
263 | "@esbuild/openbsd-x64@0.25.12":
264 | version "0.25.12"
265 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679"
266 | integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==
267 |
268 | "@esbuild/openharmony-arm64@0.25.12":
269 | version "0.25.12"
270 | resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d"
271 | integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==
272 |
273 | "@esbuild/sunos-x64@0.25.12":
274 | version "0.25.12"
275 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6"
276 | integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==
277 |
278 | "@esbuild/win32-arm64@0.25.12":
279 | version "0.25.12"
280 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323"
281 | integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==
282 |
283 | "@esbuild/win32-ia32@0.25.12":
284 | version "0.25.12"
285 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267"
286 | integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==
287 |
288 | "@esbuild/win32-x64@0.25.12":
289 | version "0.25.12"
290 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5"
291 | integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==
292 |
293 | "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
294 | version "0.3.13"
295 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
296 | integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
297 | dependencies:
298 | "@jridgewell/sourcemap-codec" "^1.5.0"
299 | "@jridgewell/trace-mapping" "^0.3.24"
300 |
301 | "@jridgewell/remapping@^2.3.5":
302 | version "2.3.5"
303 | resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1"
304 | integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
305 | dependencies:
306 | "@jridgewell/gen-mapping" "^0.3.5"
307 | "@jridgewell/trace-mapping" "^0.3.24"
308 |
309 | "@jridgewell/resolve-uri@^3.1.0":
310 | version "3.1.2"
311 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
312 | integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
313 |
314 | "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
315 | version "1.5.5"
316 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
317 | integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
318 |
319 | "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
320 | version "0.3.31"
321 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
322 | integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
323 | dependencies:
324 | "@jridgewell/resolve-uri" "^3.1.0"
325 | "@jridgewell/sourcemap-codec" "^1.4.14"
326 |
327 | "@parcel/watcher-android-arm64@2.5.1":
328 | version "2.5.1"
329 | resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1"
330 | integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==
331 |
332 | "@parcel/watcher-darwin-arm64@2.5.1":
333 | version "2.5.1"
334 | resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67"
335 | integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
336 |
337 | "@parcel/watcher-darwin-x64@2.5.1":
338 | version "2.5.1"
339 | resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8"
340 | integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==
341 |
342 | "@parcel/watcher-freebsd-x64@2.5.1":
343 | version "2.5.1"
344 | resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b"
345 | integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==
346 |
347 | "@parcel/watcher-linux-arm-glibc@2.5.1":
348 | version "2.5.1"
349 | resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1"
350 | integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==
351 |
352 | "@parcel/watcher-linux-arm-musl@2.5.1":
353 | version "2.5.1"
354 | resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e"
355 | integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==
356 |
357 | "@parcel/watcher-linux-arm64-glibc@2.5.1":
358 | version "2.5.1"
359 | resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30"
360 | integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==
361 |
362 | "@parcel/watcher-linux-arm64-musl@2.5.1":
363 | version "2.5.1"
364 | resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2"
365 | integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==
366 |
367 | "@parcel/watcher-linux-x64-glibc@2.5.1":
368 | version "2.5.1"
369 | resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e"
370 | integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==
371 |
372 | "@parcel/watcher-linux-x64-musl@2.5.1":
373 | version "2.5.1"
374 | resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee"
375 | integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==
376 |
377 | "@parcel/watcher-win32-arm64@2.5.1":
378 | version "2.5.1"
379 | resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243"
380 | integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==
381 |
382 | "@parcel/watcher-win32-ia32@2.5.1":
383 | version "2.5.1"
384 | resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6"
385 | integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==
386 |
387 | "@parcel/watcher-win32-x64@2.5.1":
388 | version "2.5.1"
389 | resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947"
390 | integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
391 |
392 | "@parcel/watcher@^2.4.1":
393 | version "2.5.1"
394 | resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200"
395 | integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==
396 | dependencies:
397 | detect-libc "^1.0.3"
398 | is-glob "^4.0.3"
399 | micromatch "^4.0.5"
400 | node-addon-api "^7.0.0"
401 | optionalDependencies:
402 | "@parcel/watcher-android-arm64" "2.5.1"
403 | "@parcel/watcher-darwin-arm64" "2.5.1"
404 | "@parcel/watcher-darwin-x64" "2.5.1"
405 | "@parcel/watcher-freebsd-x64" "2.5.1"
406 | "@parcel/watcher-linux-arm-glibc" "2.5.1"
407 | "@parcel/watcher-linux-arm-musl" "2.5.1"
408 | "@parcel/watcher-linux-arm64-glibc" "2.5.1"
409 | "@parcel/watcher-linux-arm64-musl" "2.5.1"
410 | "@parcel/watcher-linux-x64-glibc" "2.5.1"
411 | "@parcel/watcher-linux-x64-musl" "2.5.1"
412 | "@parcel/watcher-win32-arm64" "2.5.1"
413 | "@parcel/watcher-win32-ia32" "2.5.1"
414 | "@parcel/watcher-win32-x64" "2.5.1"
415 |
416 | "@rolldown/pluginutils@1.0.0-beta.53":
417 | version "1.0.0-beta.53"
418 | resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz#c57a5234ae122671aff6fe72e673a7ed90f03f87"
419 | integrity sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==
420 |
421 | "@rollup/rollup-android-arm-eabi@4.53.3":
422 | version "4.53.3"
423 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz#7e478b66180c5330429dd161bf84dad66b59c8eb"
424 | integrity sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==
425 |
426 | "@rollup/rollup-android-arm64@4.53.3":
427 | version "4.53.3"
428 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz#2b025510c53a5e3962d3edade91fba9368c9d71c"
429 | integrity sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==
430 |
431 | "@rollup/rollup-darwin-arm64@4.53.3":
432 | version "4.53.3"
433 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz#3577c38af68ccf34c03e84f476bfd526abca10a0"
434 | integrity sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==
435 |
436 | "@rollup/rollup-darwin-x64@4.53.3":
437 | version "4.53.3"
438 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz#2bf5f2520a1f3b551723d274b9669ba5b75ed69c"
439 | integrity sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==
440 |
441 | "@rollup/rollup-freebsd-arm64@4.53.3":
442 | version "4.53.3"
443 | resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz#4bb9cc80252564c158efc0710153c71633f1927c"
444 | integrity sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==
445 |
446 | "@rollup/rollup-freebsd-x64@4.53.3":
447 | version "4.53.3"
448 | resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz#2301289094d49415a380cf942219ae9d8b127440"
449 | integrity sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==
450 |
451 | "@rollup/rollup-linux-arm-gnueabihf@4.53.3":
452 | version "4.53.3"
453 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz#1d03d776f2065e09fc141df7d143476e94acca88"
454 | integrity sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==
455 |
456 | "@rollup/rollup-linux-arm-musleabihf@4.53.3":
457 | version "4.53.3"
458 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz#8623de0e040b2fd52a541c602688228f51f96701"
459 | integrity sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==
460 |
461 | "@rollup/rollup-linux-arm64-gnu@4.53.3":
462 | version "4.53.3"
463 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz#ce2d1999bc166277935dde0301cde3dd0417fb6e"
464 | integrity sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==
465 |
466 | "@rollup/rollup-linux-arm64-musl@4.53.3":
467 | version "4.53.3"
468 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz#88c2523778444da952651a2219026416564a4899"
469 | integrity sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==
470 |
471 | "@rollup/rollup-linux-loong64-gnu@4.53.3":
472 | version "4.53.3"
473 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz#578ca2220a200ac4226c536c10c8cc6e4f276714"
474 | integrity sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==
475 |
476 | "@rollup/rollup-linux-ppc64-gnu@4.53.3":
477 | version "4.53.3"
478 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz#aa338d3effd4168a20a5023834a74ba2c3081293"
479 | integrity sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==
480 |
481 | "@rollup/rollup-linux-riscv64-gnu@4.53.3":
482 | version "4.53.3"
483 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz#16ba582f9f6cff58119aa242782209b1557a1508"
484 | integrity sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==
485 |
486 | "@rollup/rollup-linux-riscv64-musl@4.53.3":
487 | version "4.53.3"
488 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz#e404a77ebd6378483888b8064c703adb011340ab"
489 | integrity sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==
490 |
491 | "@rollup/rollup-linux-s390x-gnu@4.53.3":
492 | version "4.53.3"
493 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz#92ad52d306227c56bec43d96ad2164495437ffe6"
494 | integrity sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==
495 |
496 | "@rollup/rollup-linux-x64-gnu@4.53.3":
497 | version "4.53.3"
498 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz#fd0dea3bb9aa07e7083579f25e1c2285a46cb9fa"
499 | integrity sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==
500 |
501 | "@rollup/rollup-linux-x64-musl@4.53.3":
502 | version "4.53.3"
503 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz#37a3efb09f18d555f8afc490e1f0444885de8951"
504 | integrity sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==
505 |
506 | "@rollup/rollup-openharmony-arm64@4.53.3":
507 | version "4.53.3"
508 | resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz#c489bec9f4f8320d42c9b324cca220c90091c1f7"
509 | integrity sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==
510 |
511 | "@rollup/rollup-win32-arm64-msvc@4.53.3":
512 | version "4.53.3"
513 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz#152832b5f79dc22d1606fac3db946283601b7080"
514 | integrity sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==
515 |
516 | "@rollup/rollup-win32-ia32-msvc@4.53.3":
517 | version "4.53.3"
518 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz#54d91b2bb3bf3e9f30d32b72065a4e52b3a172a5"
519 | integrity sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==
520 |
521 | "@rollup/rollup-win32-x64-gnu@4.53.3":
522 | version "4.53.3"
523 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz#df9df03e61a003873efec8decd2034e7f135c71e"
524 | integrity sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==
525 |
526 | "@rollup/rollup-win32-x64-msvc@4.53.3":
527 | version "4.53.3"
528 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz#38ae84f4c04226c1d56a3b17296ef1e0460ecdfe"
529 | integrity sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==
530 |
531 | "@types/babel__core@^7.20.5":
532 | version "7.20.5"
533 | resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
534 | integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
535 | dependencies:
536 | "@babel/parser" "^7.20.7"
537 | "@babel/types" "^7.20.7"
538 | "@types/babel__generator" "*"
539 | "@types/babel__template" "*"
540 | "@types/babel__traverse" "*"
541 |
542 | "@types/babel__generator@*":
543 | version "7.27.0"
544 | resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9"
545 | integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==
546 | dependencies:
547 | "@babel/types" "^7.0.0"
548 |
549 | "@types/babel__template@*":
550 | version "7.4.4"
551 | resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f"
552 | integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==
553 | dependencies:
554 | "@babel/parser" "^7.1.0"
555 | "@babel/types" "^7.0.0"
556 |
557 | "@types/babel__traverse@*":
558 | version "7.28.0"
559 | resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74"
560 | integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==
561 | dependencies:
562 | "@babel/types" "^7.28.2"
563 |
564 | "@types/estree@1.0.8":
565 | version "1.0.8"
566 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
567 | integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
568 |
569 | "@vitejs/plugin-react@^5.1.1":
570 | version "5.1.2"
571 | resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz#46f47be184c05a18839cb8705d79578b469ac6eb"
572 | integrity sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==
573 | dependencies:
574 | "@babel/core" "^7.28.5"
575 | "@babel/plugin-transform-react-jsx-self" "^7.27.1"
576 | "@babel/plugin-transform-react-jsx-source" "^7.27.1"
577 | "@rolldown/pluginutils" "1.0.0-beta.53"
578 | "@types/babel__core" "^7.20.5"
579 | react-refresh "^0.18.0"
580 |
581 | baseline-browser-mapping@^2.9.0:
582 | version "2.9.7"
583 | resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz#d36ce64f2a2c468f6f743c8db495d319120007db"
584 | integrity sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==
585 |
586 | braces@^3.0.3:
587 | version "3.0.3"
588 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
589 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
590 | dependencies:
591 | fill-range "^7.1.1"
592 |
593 | browserslist@^4.24.0:
594 | version "4.28.1"
595 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95"
596 | integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==
597 | dependencies:
598 | baseline-browser-mapping "^2.9.0"
599 | caniuse-lite "^1.0.30001759"
600 | electron-to-chromium "^1.5.263"
601 | node-releases "^2.0.27"
602 | update-browserslist-db "^1.2.0"
603 |
604 | caniuse-lite@^1.0.30001759:
605 | version "1.0.30001760"
606 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz#bdd1960fafedf8d5f04ff16e81460506ff9b798f"
607 | integrity sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==
608 |
609 | chokidar@^4.0.0:
610 | version "4.0.3"
611 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30"
612 | integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==
613 | dependencies:
614 | readdirp "^4.0.1"
615 |
616 | classnames@^2.5.1:
617 | version "2.5.1"
618 | resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
619 | integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
620 |
621 | convert-source-map@^2.0.0:
622 | version "2.0.0"
623 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
624 | integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
625 |
626 | debug@^4.1.0, debug@^4.3.1:
627 | version "4.4.3"
628 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
629 | integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
630 | dependencies:
631 | ms "^2.1.3"
632 |
633 | detect-libc@^1.0.3:
634 | version "1.0.3"
635 | resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
636 | integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
637 |
638 | electron-to-chromium@^1.5.263:
639 | version "1.5.267"
640 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7"
641 | integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==
642 |
643 | esbuild@^0.25.0:
644 | version "0.25.12"
645 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5"
646 | integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==
647 | optionalDependencies:
648 | "@esbuild/aix-ppc64" "0.25.12"
649 | "@esbuild/android-arm" "0.25.12"
650 | "@esbuild/android-arm64" "0.25.12"
651 | "@esbuild/android-x64" "0.25.12"
652 | "@esbuild/darwin-arm64" "0.25.12"
653 | "@esbuild/darwin-x64" "0.25.12"
654 | "@esbuild/freebsd-arm64" "0.25.12"
655 | "@esbuild/freebsd-x64" "0.25.12"
656 | "@esbuild/linux-arm" "0.25.12"
657 | "@esbuild/linux-arm64" "0.25.12"
658 | "@esbuild/linux-ia32" "0.25.12"
659 | "@esbuild/linux-loong64" "0.25.12"
660 | "@esbuild/linux-mips64el" "0.25.12"
661 | "@esbuild/linux-ppc64" "0.25.12"
662 | "@esbuild/linux-riscv64" "0.25.12"
663 | "@esbuild/linux-s390x" "0.25.12"
664 | "@esbuild/linux-x64" "0.25.12"
665 | "@esbuild/netbsd-arm64" "0.25.12"
666 | "@esbuild/netbsd-x64" "0.25.12"
667 | "@esbuild/openbsd-arm64" "0.25.12"
668 | "@esbuild/openbsd-x64" "0.25.12"
669 | "@esbuild/openharmony-arm64" "0.25.12"
670 | "@esbuild/sunos-x64" "0.25.12"
671 | "@esbuild/win32-arm64" "0.25.12"
672 | "@esbuild/win32-ia32" "0.25.12"
673 | "@esbuild/win32-x64" "0.25.12"
674 |
675 | escalade@^3.2.0:
676 | version "3.2.0"
677 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
678 | integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
679 |
680 | fdir@^6.5.0:
681 | version "6.5.0"
682 | resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
683 | integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
684 |
685 | fill-range@^7.1.1:
686 | version "7.1.1"
687 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
688 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
689 | dependencies:
690 | to-regex-range "^5.0.1"
691 |
692 | fsevents@~2.3.2, fsevents@~2.3.3:
693 | version "2.3.3"
694 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
695 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
696 |
697 | gensync@^1.0.0-beta.2:
698 | version "1.0.0-beta.2"
699 | resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
700 | integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
701 |
702 | globals@^16.5.0:
703 | version "16.5.0"
704 | resolved "https://registry.yarnpkg.com/globals/-/globals-16.5.0.tgz#ccf1594a437b97653b2be13ed4d8f5c9f850cac1"
705 | integrity sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==
706 |
707 | immutable@^5.0.2:
708 | version "5.1.4"
709 | resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.4.tgz#e3f8c1fe7b567d56cf26698f31918c241dae8c1f"
710 | integrity sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==
711 |
712 | is-extglob@^2.1.1:
713 | version "2.1.1"
714 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
715 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
716 |
717 | is-glob@^4.0.3:
718 | version "4.0.3"
719 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
720 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
721 | dependencies:
722 | is-extglob "^2.1.1"
723 |
724 | is-number@^7.0.0:
725 | version "7.0.0"
726 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
727 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
728 |
729 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
730 | version "4.0.0"
731 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
732 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
733 |
734 | jsesc@^3.0.2:
735 | version "3.1.0"
736 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
737 | integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
738 |
739 | json5@^2.2.3:
740 | version "2.2.3"
741 | resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
742 | integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
743 |
744 | loose-envify@^1.4.0:
745 | version "1.4.0"
746 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
747 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
748 | dependencies:
749 | js-tokens "^3.0.0 || ^4.0.0"
750 |
751 | lru-cache@^5.1.1:
752 | version "5.1.1"
753 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
754 | integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
755 | dependencies:
756 | yallist "^3.0.2"
757 |
758 | micromatch@^4.0.5:
759 | version "4.0.8"
760 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
761 | integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
762 | dependencies:
763 | braces "^3.0.3"
764 | picomatch "^2.3.1"
765 |
766 | ms@^2.1.3:
767 | version "2.1.3"
768 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
769 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
770 |
771 | nanoid@^3.3.11:
772 | version "3.3.11"
773 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
774 | integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
775 |
776 | node-addon-api@^7.0.0:
777 | version "7.1.1"
778 | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
779 | integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
780 |
781 | node-releases@^2.0.27:
782 | version "2.0.27"
783 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e"
784 | integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==
785 |
786 | object-assign@^4.1.1:
787 | version "4.1.1"
788 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
789 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
790 |
791 | picocolors@^1.1.1:
792 | version "1.1.1"
793 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
794 | integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
795 |
796 | picomatch@^2.3.1:
797 | version "2.3.1"
798 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
799 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
800 |
801 | picomatch@^4.0.3:
802 | version "4.0.3"
803 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
804 | integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
805 |
806 | postcss@^8.5.6:
807 | version "8.5.6"
808 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
809 | integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
810 | dependencies:
811 | nanoid "^3.3.11"
812 | picocolors "^1.1.1"
813 | source-map-js "^1.2.1"
814 |
815 | prettier@3.2.4:
816 | version "3.2.4"
817 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.4.tgz#4723cadeac2ce7c9227de758e5ff9b14e075f283"
818 | integrity sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==
819 |
820 | prop-types@^15.7.2:
821 | version "15.8.1"
822 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
823 | integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
824 | dependencies:
825 | loose-envify "^1.4.0"
826 | object-assign "^4.1.1"
827 | react-is "^16.13.1"
828 |
829 | react-dom@^19.2.0:
830 | version "19.2.3"
831 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.3.tgz#f0b61d7e5c4a86773889fcc1853af3ed5f215b17"
832 | integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==
833 | dependencies:
834 | scheduler "^0.27.0"
835 |
836 | react-fast-compare@^3.1.1:
837 | version "3.2.2"
838 | resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
839 | integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
840 |
841 | react-helmet@^6.1.0:
842 | version "6.1.0"
843 | resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726"
844 | integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==
845 | dependencies:
846 | object-assign "^4.1.1"
847 | prop-types "^15.7.2"
848 | react-fast-compare "^3.1.1"
849 | react-side-effect "^2.1.0"
850 |
851 | react-is@^16.13.1:
852 | version "16.13.1"
853 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
854 | integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
855 |
856 | react-refresh@^0.18.0:
857 | version "0.18.0"
858 | resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062"
859 | integrity sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==
860 |
861 | react-side-effect@^2.1.0:
862 | version "2.1.2"
863 | resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a"
864 | integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==
865 |
866 | react@^19.2.0:
867 | version "19.2.3"
868 | resolved "https://registry.yarnpkg.com/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8"
869 | integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==
870 |
871 | readdirp@^4.0.1:
872 | version "4.1.2"
873 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d"
874 | integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
875 |
876 | rollup@^4.43.0:
877 | version "4.53.3"
878 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.3.tgz#dbc8cd8743b38710019fb8297e8d7a76e3faa406"
879 | integrity sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==
880 | dependencies:
881 | "@types/estree" "1.0.8"
882 | optionalDependencies:
883 | "@rollup/rollup-android-arm-eabi" "4.53.3"
884 | "@rollup/rollup-android-arm64" "4.53.3"
885 | "@rollup/rollup-darwin-arm64" "4.53.3"
886 | "@rollup/rollup-darwin-x64" "4.53.3"
887 | "@rollup/rollup-freebsd-arm64" "4.53.3"
888 | "@rollup/rollup-freebsd-x64" "4.53.3"
889 | "@rollup/rollup-linux-arm-gnueabihf" "4.53.3"
890 | "@rollup/rollup-linux-arm-musleabihf" "4.53.3"
891 | "@rollup/rollup-linux-arm64-gnu" "4.53.3"
892 | "@rollup/rollup-linux-arm64-musl" "4.53.3"
893 | "@rollup/rollup-linux-loong64-gnu" "4.53.3"
894 | "@rollup/rollup-linux-ppc64-gnu" "4.53.3"
895 | "@rollup/rollup-linux-riscv64-gnu" "4.53.3"
896 | "@rollup/rollup-linux-riscv64-musl" "4.53.3"
897 | "@rollup/rollup-linux-s390x-gnu" "4.53.3"
898 | "@rollup/rollup-linux-x64-gnu" "4.53.3"
899 | "@rollup/rollup-linux-x64-musl" "4.53.3"
900 | "@rollup/rollup-openharmony-arm64" "4.53.3"
901 | "@rollup/rollup-win32-arm64-msvc" "4.53.3"
902 | "@rollup/rollup-win32-ia32-msvc" "4.53.3"
903 | "@rollup/rollup-win32-x64-gnu" "4.53.3"
904 | "@rollup/rollup-win32-x64-msvc" "4.53.3"
905 | fsevents "~2.3.2"
906 |
907 | sass@^1.70.0:
908 | version "1.96.0"
909 | resolved "https://registry.yarnpkg.com/sass/-/sass-1.96.0.tgz#dca45b6b08ae829500f448124afc7c15150bbb34"
910 | integrity sha512-8u4xqqUeugGNCYwr9ARNtQKTOj4KmYiJAVKXf2CTIivTCR51j96htbMKWDru8H5SaQWpyVgTfOF8Ylyf5pun1Q==
911 | dependencies:
912 | chokidar "^4.0.0"
913 | immutable "^5.0.2"
914 | source-map-js ">=0.6.2 <2.0.0"
915 | optionalDependencies:
916 | "@parcel/watcher" "^2.4.1"
917 |
918 | scheduler@^0.27.0:
919 | version "0.27.0"
920 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
921 | integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
922 |
923 | semver@^6.3.1:
924 | version "6.3.1"
925 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
926 | integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
927 |
928 | "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1:
929 | version "1.2.1"
930 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
931 | integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
932 |
933 | tinyglobby@^0.2.15:
934 | version "0.2.15"
935 | resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
936 | integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
937 | dependencies:
938 | fdir "^6.5.0"
939 | picomatch "^4.0.3"
940 |
941 | to-regex-range@^5.0.1:
942 | version "5.0.1"
943 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
944 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
945 | dependencies:
946 | is-number "^7.0.0"
947 |
948 | update-browserslist-db@^1.2.0:
949 | version "1.2.2"
950 | resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz#cfb4358afa08b3d5731a2ecd95eebf4ddef8033e"
951 | integrity sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==
952 | dependencies:
953 | escalade "^3.2.0"
954 | picocolors "^1.1.1"
955 |
956 | uuid@^9.0.1:
957 | version "9.0.1"
958 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
959 | integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
960 |
961 | vite@^7.2.4:
962 | version "7.2.7"
963 | resolved "https://registry.yarnpkg.com/vite/-/vite-7.2.7.tgz#0789a4c3206081699f34a9ecca2dda594a07478e"
964 | integrity sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==
965 | dependencies:
966 | esbuild "^0.25.0"
967 | fdir "^6.5.0"
968 | picomatch "^4.0.3"
969 | postcss "^8.5.6"
970 | rollup "^4.43.0"
971 | tinyglobby "^0.2.15"
972 | optionalDependencies:
973 | fsevents "~2.3.3"
974 |
975 | yallist@^3.0.2:
976 | version "3.1.1"
977 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
978 | integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
979 |
--------------------------------------------------------------------------------