diff --git a/.gitignore b/.gitignore
index 41e1305..dbe7f58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,7 @@
.venv
-node_modules
-.idea
__pycache__/
*.pyc
examples/*
!examples/*.yaml
!examples/*.png
+.idea
diff --git a/README.md b/README.md
index cf88ca2..4337d74 100644
--- a/README.md
+++ b/README.md
@@ -1,98 +1,3 @@
# MakeCode Arcade to App
Convert your MakeCode Arcade games into a standalone offline executable!
-
-## Install
-
-### Dependencies
-
-* Python (earliest version tested with is 3.12)
-* Node (earliest version tested with is v20)
-* Rust (earliest version tested with is 1.86) if building with Tauri
-
-### Steps
-
-1. Clone repo.
-2. Create virtual environment and install Python dependencies in [
- `requirements.txt`](requirements.txt) into it.
-3. Install JavaScript dependencies with `yarn`.
-
-## Usage
-
-Quick start:
-
-1. Create a YAML configuration file. See
- [`Racers to Electron.yaml`](examples/Racers%20to%20Electron.yaml) for an
- example.
-2. Run the script with `python src/main.py "examples/Racers to Electron.yaml"`.
-
-### Input
-
-A YAML configuration file is used. Check out the examples in the
-[`examples`](examples) directory to see how it's used, as it's
-self-explanatory. Pass it as the first positional argument to the script.
-
-### Output
-
-The script will print the path to the output directory, which changes depending
-on the output specified.
-
-#### Static files
-
-If the output is set to `static`, you will get HTML, CSS, and JS files that you
-can serve from a web server. (maybe on your own website?) The script will print
-out something like this:
-
-```commandline
-...
-dist/assets/index-D3F42Pqm.js 225.78 kB │ gzip: 70.51 kB
-✓ built in 532ms
-Done in 1.60s.
-2025-05-15 20:28:48,660 - __main__ - INFO - Static website files are at C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-website\dist
-2025-05-15 20:28:48,660 - __main__ - INFO - Build finished
-```
-
-You will find the HTML, CSS, and JS files at the specified path, where
-`index.html` is the entry point.
-
-#### Executable with Electron
-
-If the output is set to `electron`, you will get an executable using the
-Electron framework. The script will print out something like this:
-
-```commandline
-...
-› Artifacts available at: C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-electron\out\make
-√ Running postMake hook
-Done in 50.25s.
-2025-05-15 20:32:22,652 - __main__ - INFO - Electron app executables are at C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-electron\out
-2025-05-15 20:32:22,652 - __main__ - INFO - Build finished
-```
-
-You will find the directory with the executable and supporting files at the
-specified path. (all of those files alongside the executable are needed, so
-it's recommended to zip up the entire directory to distribute) You will also
-find an installer in the `make` subdirectory.
-
-#### Executable with Tauri (recommended option)
-
-> This is the recommended option due to the small size and standalone nature of
-> the executable. (~10 mb single executable with Tauri compared to almost 300
-> mb
-> in files with Electron)
-
-If the output is set to `tauri`, you will get an executable using the Tauri
-framework. The script will print out something like this:
-
-```commandline
-...
- C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-tauri\src-tauri\target\release\bundle\nsis\Racers v1.3.2_1.3.2_x64-setup.exe
-
-Done in 37.90s.
-2025-05-15 23:39:14,612 - __main__ - INFO - Tauri app executables are at C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-tauri\src-tauri\target\release
-2025-05-15 23:39:14,612 - __main__ - INFO - Build finished
-```
-
-You will find the executable at the specified path. (only the executable is
-needed, no supporting files necessary) You will also find installers in the
-`bundle` subdirectory.
diff --git a/examples/Racers to Electron.yaml b/examples/Racers to Electron.yaml
deleted file mode 100644
index 60df016..0000000
--- a/examples/Racers to Electron.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-# Example configuration file for MakeCode Arcade to App to compile to an executable
-
-name: Racers
-description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade!
- For the MakeCode Arcade Mini Game Jam #3."
-author: UnsignedArduino
-version: 1.3.2
-# This is what the window title will be
-# This will also be used for the executable file name for Electron outputs
-# You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct values
-title: "{NAME} v{VERSION}"
-
-# Game source - where the game code is located - this will download / copy everything
-# Can be a GitHub repository (.git ending is optional) that specifies something to check out,
-# a MakeCode Arcade share link, (ex. https://arcade.makecode.com/84426-33815-03715-00484)
-# or a path (ex. F:/Racers)
-#source:
-# url: https://github.com/UnsignedArduino/Racers
-# checkout: master # Could be a branch, tag, commit hash, etc. (it's passed to `git checkout` directly)
-source: https://arcade.makecode.com/84426-33815-03715-00484
-#source: F:/Racers
-
-# Game icon - this will be the icon of the tab or executable
-# Either absolute path to a file, a relative path from this file, or a URL
-#icon: Racers icon.png
-icon: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png
-
-# Output format - whether to output static files, an Electron app, or a Tauri app.
-# output: static
-output: electron
-# output: tauri
diff --git a/examples/Racers to Tauri.yaml b/examples/Racers to Tauri.yaml
deleted file mode 100644
index dc2741a..0000000
--- a/examples/Racers to Tauri.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-# Example configuration file for MakeCode Arcade to App to compile to an executable
-
-name: Racers
-description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade!
- For the MakeCode Arcade Mini Game Jam #3."
-author: UnsignedArduino
-version: 1.3.2
-# This is what the window title will be
-# This will also be used for the executable file name for Electron outputs
-# You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct values
-title: "{NAME} v{VERSION}"
-
-# Game source - where the game code is located - this will download / copy everything
-# Can be a GitHub repository (.git ending is optional) that specifies something to check out,
-# a MakeCode Arcade share link, (ex. https://arcade.makecode.com/84426-33815-03715-00484)
-# or a path (ex. F:/Racers)
-#source:
-# url: https://github.com/UnsignedArduino/Racers
-# checkout: master # Could be a branch, tag, commit hash, etc. (it's passed to `git checkout` directly)
-source: https://arcade.makecode.com/84426-33815-03715-00484
-#source: F:/Racers
-
-# Game icon - this will be the icon of the tab or executable
-# Either absolute path to a file, a relative path from this file, or a URL
-#icon: Racers icon.png
-icon: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png
-
-# Output format - whether to output static files, an Electron app, or a Tauri app.
-# output: static
-# output: electron
-output: tauri
diff --git a/examples/Racers to static files.yaml b/examples/Racers to static files.yaml
deleted file mode 100644
index 1e6443b..0000000
--- a/examples/Racers to static files.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-# Example configuration file for MakeCode Arcade to App to compile to an executable
-
-name: Racers
-description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade!
- For the MakeCode Arcade Mini Game Jam #3."
-author: UnsignedArduino
-version: 1.3.2
-# This is what the window title will be
-# This will also be used for the executable file name for Electron outputs
-# You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct values
-title: "{NAME} v{VERSION}"
-
-# Game source - where the game code is located - this will download / copy everything
-# Can be a GitHub repository (.git ending is optional) that specifies something to check out,
-# a MakeCode Arcade share link, (ex. https://arcade.makecode.com/84426-33815-03715-00484)
-# or a path (ex. F:/Racers)
-#source:
-# url: https://github.com/UnsignedArduino/Racers
-# checkout: master # Could be a branch, tag, commit hash, etc. (it's passed to `git checkout` directly)
-source: https://arcade.makecode.com/84426-33815-03715-00484
-#source: F:/Racers
-
-# Game icon - this will be the icon of the tab or executable
-# Either absolute path to a file, a relative path from this file, or a URL
-#icon: Racers icon.png
-icon: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png
-
-# Output format - whether to output static files, an Electron app, or a Tauri app.
-output: static
-# output: electron
-# output: tauri
diff --git a/examples/Racers to static.yaml b/examples/Racers to static.yaml
new file mode 100644
index 0000000..9a1fa33
--- /dev/null
+++ b/examples/Racers to static.yaml
@@ -0,0 +1,61 @@
+# Example configuration file for MakeCode Arcade to App to compile to static
+# HTML, CSS, and JS files
+
+# Config version (single number, incremented if breaking change to schema is
+# made)
+version: 1
+
+project:
+ name: Racers
+ path_friendly_name: racers
+ description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade!
+ For the MakeCode Arcade Mini Game Jam #3."
+ author: UnsignedArduino
+ version: 1.3.2
+ # This is what the window title will be
+ # This will also be used for the executable file name for Electron outputs
+ # You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct
+ # values
+ title: "{NAME} v{VERSION}"
+
+inputs:
+ code:
+ # Can be share_link, github, or path
+ type: share_link
+ value: https://arcade.makecode.com/84426-33815-03715-00484
+
+ # code:
+ # type: github
+ # value: https://github.com/UnsignedArduino/Racers
+ # checkout: master
+
+ # code:
+ # type: path
+ # value: "E:/Racers"
+
+ assets:
+ icon:
+ # Can be url or path
+ type: url
+ value: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png
+
+# icon:
+# type: path
+# value: "./examples/Racers icon.png"
+
+# Path where we
+build_dir: "./examples/Racers-build"
+
+# You can have multiple outputs
+# Currently available options are static, electron, and tauri
+outputs:
+ - type: static
+# - type: electron
+# window:
+# width: 640
+# height: 480
+# - type: tauri
+# identifier: com.unsignedarduino.racers
+# window:
+# width: 640
+# height: 480
diff --git a/package.json b/package.json
deleted file mode 100644
index 4dcf6e3..0000000
--- a/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "dependencies": {
- "makecode": "^1.3.4"
- },
- "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
-}
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..9664ae0
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,25 @@
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[project]
+name = "mkcd2app"
+version = "0.1.0"
+description = "Convert MakeCode Arcade games into static websites, Electron, or Tauri apps"
+readme = "README.md"
+requires-python = ">=3.14"
+# We are pinning redun because we monkey patch something
+dependencies = [
+ "pydantic",
+ "pyyaml",
+ "redun==0.44.1",
+ "requests",
+ "beautifulsoup4",
+ "pillow",
+]
+
+[project.scripts]
+mkcd2app = "mkcd2app.main:main"
+
+[tool.hatch.build.targets.wheel]
+packages = ["src/mkcd2app"]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 437be0e..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-pyyaml
-requests
-beautifulsoup4
-pillow
diff --git a/src/convert/mkcd_to_website/config.py b/src/convert/mkcd_to_website/config.py
deleted file mode 100644
index 2d01b3c..0000000
--- a/src/convert/mkcd_to_website/config.py
+++ /dev/null
@@ -1,135 +0,0 @@
-import logging
-from dataclasses import dataclass
-from enum import Enum
-from pathlib import Path
-from typing import Optional
-from urllib.parse import urlparse
-
-import yaml
-
-from utils.logger import create_logger
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-
-class SourceType(Enum):
- GITHUB = "github"
- SHARE_LINK = "share_link"
- PATH = "path"
-
-
-class IconSourceType(Enum):
- PATH = "path"
- URL = "url"
-
-
-class OutputType(Enum):
- STATIC = "static"
- ELECTRON = "electron"
- TAURI = "tauri"
-
-
-@dataclass
-class Config:
- """
- Configuration class.
- """
- name: str
- description: str
- author: str
- version: str
- title: str
-
- source: str
- source_type: SourceType
- source_checkout: Optional[str] = None # For GitHub sources
-
- icon: Optional[Path | str] = None
- icon_source_type: Optional[IconSourceType] = None
-
- output: OutputType = OutputType.STATIC
-
-
-# https://stackoverflow.com/a/36283503/10291933
-def is_valid_url(url, qualifying=('scheme', 'netloc')):
- tokens = urlparse(url)
- return all(getattr(tokens, qualifying_attr)
- for qualifying_attr in qualifying)
-
-
-def determine_source_type(source: str | dict) -> SourceType:
- """
- Determines the source type based on the provided source string.
-
- :param source: The source string or dictionary.
- :return: The SourceType enum value.
- """
- # We could do more checks for validity / strictness here
- if type(source) is not str:
- # Should have url and checkout
- return SourceType.GITHUB
- elif is_valid_url(source):
- return SourceType.SHARE_LINK
- else:
- return SourceType.PATH
-
-
-def determine_icon_source_type(icon: str) -> IconSourceType:
- """
- Determines the icon source type based on the provided icon string.
-
- :param icon: The icon string.
- :return: The IconSourceType enum value.
- """
- # We could do more checks for validity / strictness here
- if is_valid_url(icon):
- return IconSourceType.URL
- else:
- return IconSourceType.PATH
-
-
-def parse_config(yaml_text: str, cwd: Path) -> Config:
- """
- Parses the YAML configuration file and returns a Config object.
-
- :param yaml_text: The YAML configuration text.
- :param cwd: The current working directory to resolve relative paths.
- :return: A Config object.
- """
- logger.debug(f"Parsing YAML configuration")
- result = yaml.safe_load(yaml_text)
-
- src = result.get("source")
- src_type = determine_source_type(src)
- logger.debug(f"Determined source type for {src} is {src_type}")
- src_checkout = None
- if type(src) is not str:
- src_checkout = src.get("checkout")
- src = src.get("url")
- logger.debug(f"Complex GitHub source detected - will checkout {src_checkout} "
- f"for url {src}")
-
- icon = result.get("icon")
- icon_source_type = None if icon is None else determine_icon_source_type(icon)
- if icon_source_type == IconSourceType.PATH:
- icon = Path(icon)
- if not icon.is_absolute():
- icon = cwd / icon
- logger.debug(f"Determined icon source type for {icon} is {icon_source_type}")
-
- config = Config(
- name=result.get("name"),
- description=result.get("description"),
- author=result.get("author"),
- version=result.get("version"),
- title=result.get("title"),
- source=src,
- source_type=src_type,
- source_checkout=src_checkout,
- icon=icon,
- icon_source_type=icon_source_type,
- output=OutputType(result.get("output", "static").lower())
- )
- config.title = config.title.format(NAME=config.name, VERSION=config.version, AUTHOR=config.author)
- logger.debug(f"Parsed configuration: {config}")
- return config
diff --git a/src/convert/mkcd_to_website/source.py b/src/convert/mkcd_to_website/source.py
deleted file mode 100644
index d5190c3..0000000
--- a/src/convert/mkcd_to_website/source.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import logging
-import shutil
-from pathlib import Path
-from typing import Optional
-
-from convert.mkcd_to_website.config import Config, SourceType
-from utils.cmd import run_command, run_shell_command
-from utils.logger import create_logger
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-
-def download_source(config: Config, cwd: Path,
- no_cache: Optional[bool] = False) -> Path:
- """
- Downloads the source code based on the provided configuration.
-
- :param config: The configuration object containing source information.
- :param cwd: The current working directory where the source code folder will be
- downloaded.
- :param no_cache: If True, forces a fresh download of the source code.
- :return: The path to the downloaded source code.
- """
- source_code_path = cwd / f"{config.name} source"
- if no_cache:
- logger.debug("Checking for existing source code to remove")
- if source_code_path.exists():
- logger.debug(f"Removing {source_code_path}")
- try:
- shutil.rmtree(source_code_path)
- except PermissionError as e:
- logger.error(e)
- logger.error("Permission denied. If it's a .git object, you may need "
- "to delete it with admin/superuser privileges.")
- raise e
- elif source_code_path.exists():
- logger.debug(f"Source code already exists at {source_code_path}")
- if config.source_type == SourceType.GITHUB:
- logger.debug("Checking for updates")
- run_command(["git", "checkout", config.source_checkout],
- cwd=source_code_path)
- run_command(["git", "pull"], cwd=source_code_path)
- return source_code_path
- if config.source_type == SourceType.GITHUB:
- logger.info(f"Downloading source from GitHub")
- # Assume it's `git clone`able
- run_command(["git", "clone", config.source, source_code_path], cwd=cwd)
- logger.info(f"Checking out {config.source_checkout}")
- run_command(["git", "checkout", config.source_checkout], cwd=source_code_path)
- elif config.source_type == SourceType.SHARE_LINK:
- logger.info(f"Downloading source from share link")
- source_code_path.mkdir(parents=True, exist_ok=True)
- run_shell_command(f"npx mkc download {config.source}", cwd=source_code_path)
- elif config.source_type == SourceType.PATH:
- logger.info(f"Copying source from path")
- source_code_path.mkdir(parents=True, exist_ok=True)
- # Copy the contents of the source directory to the source_code_path
- source_path = Path(config.source)
- for item in source_path.iterdir():
- if item.is_dir():
- shutil.copytree(item, source_code_path / item.name)
- else:
- shutil.copy2(item, source_code_path / item.name)
- else:
- raise ValueError(f"Unknown source type {config.source_type}")
- logger.debug(f"Source code path: {source_code_path}")
- return source_code_path
diff --git a/src/convert/mkcd_to_website/website.py b/src/convert/mkcd_to_website/website.py
deleted file mode 100644
index 630c0df..0000000
--- a/src/convert/mkcd_to_website/website.py
+++ /dev/null
@@ -1,153 +0,0 @@
-import json
-import logging
-import shutil
-from io import BytesIO
-from pathlib import Path
-from typing import Callable
-
-import requests
-from PIL import Image
-from bs4 import BeautifulSoup
-
-from convert.mkcd_to_website.config import Config, IconSourceType, SourceType
-from utils.cmd import run_shell_command
-from utils.logger import create_logger
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-
-def generate_website(config: Config, prj_name: str, template_dir: Path, cwd: Path,
- bin_js_path: Path):
- """
- Generate the website by initializing a React TS Vite project, copying the necessary
- files, and substituting the correct values in.
-
- :param config: The configuration object containing the project information.
- :param prj_name: The name of the project.
- :param template_dir: The directory containing the template files.
- :param cwd: The current working directory where the project will be created.
- :param bin_js_path: The path to the binary.js file.
- """
- logger.debug(f"Creating React TS Vite project for {prj_name}")
- # Initialize a React TS Vite project
- if (cwd / prj_name).exists():
- logger.debug(f"Project {prj_name} already exists, continuing...")
- else:
- run_shell_command(f"yarn create vite {prj_name} -t react-ts --no-interactive --no-rolldown", cwd=cwd)
- # Start copying files from template
- old_dir = template_dir
- new_dir = cwd / prj_name
-
- def copy_template(file_name: str, callback: Callable[[str], str] = lambda x: x):
- (new_dir / file_name).write_text(callback((old_dir / file_name).read_text()))
-
- logger.debug(f"Copying website files from {old_dir} to {new_dir}")
- # Copy index.html and substitute the title
- copy_template("index.html", lambda x: x.format(TITLE=config.title))
- # Modify package.json
- package_json = json.loads((old_dir / "package.json").read_text())
- package_json["name"] = prj_name
- package_json["version"] = config.version
- package_json["description"] = config.description
- package_json["author"] = config.author
- package_json["scripts"] = {
- "dev": "vite",
- "lint": "eslint .",
- "writeLint": "eslint --fix .",
- "format": "prettier --check .",
- "writeFormat": "prettier --write .",
- "build": "tsc -b && vite build",
- "preview": "vite preview"
- }
- (new_dir / "package.json").write_text(json.dumps(package_json, indent=2))
- # Queue dependencies to add (these will be "yarn add"ed)
- dependencies = ["react-toastify"]
- dev_dependencies = ["eslint-plugin-react-dom", "eslint-plugin-react-x", "prettier"]
- # Copy README.md
- copy_template("README.md",
- lambda x: x.format(WEBSITE_NAME=prj_name,
- SOURCE=f"{config.source} @ {config.source_checkout}" if config.source_type == SourceType.GITHUB else config.source))
- # Copy more files
- for file_name in ("vite.config.ts", "eslint.config.js", ".prettierignore",
- "tsconfig.json",
- "tsconfig.app.json", "tsconfig.node.json"):
- copy_template(file_name)
- # yarn
- run_shell_command("yarn", cwd=new_dir)
- # yarn add stuff
- run_shell_command(f"yarn add {" ".join(dependencies)}", cwd=new_dir)
- run_shell_command(f"yarn add {" ".join(dev_dependencies)} --dev", cwd=new_dir)
- # Clear public directory
- if (new_dir / "public").exists():
- shutil.rmtree(new_dir / "public")
- (new_dir / "public").mkdir(parents=True, exist_ok=True)
- # Clear src/assets directory
- if (new_dir / "src" / "assets").exists():
- shutil.rmtree(new_dir / "src" / "assets")
- # Copy src directory
- shutil.copytree(old_dir / "src", new_dir / "src", dirs_exist_ok=True)
- game_config_ts_path = new_dir / "src" / "gameConfiguration.ts"
- logger.debug(f"gameConfiguration.ts at {game_config_ts_path}")
- # Copy binary.js
- logger.debug(f"Copying binary.js from {bin_js_path}")
- shutil.copy(bin_js_path, new_dir / "public" / "binary.js")
- # Download https://trg-arcade.userpxt.io/---simulator
- logger.debug("Downloading simulator files")
- res = requests.get("https://trg-arcade.userpxt.io/---simulator")
- if res.ok:
- sim_html = res.text
- else:
- raise Exception(f"Failed to download simulator: {res.status_code} {res.reason}")
- # Analyze simulator HTML for required CSS and JS files
- logger.debug("Analyzing simulator HTML for required CSS and JS files")
- soup = BeautifulSoup(sim_html, features="html.parser")
- css_links = soup.find_all("link", rel="stylesheet")
- js_scripts = soup.find_all("script")
- logger.debug(f"Found {len(css_links)} CSS links and {len(js_scripts)} JS scripts")
- for css in css_links:
- if css.get("href"):
- css_url = css.get("href")
- logger.debug(f"Downloading CSS file: {css_url}")
- res = requests.get(css_url)
- if res.ok:
- file_name = css_url.split("/")[-1]
- # Download CSS file
- (new_dir / "public" / file_name).write_text(res.text)
- # Rewrite CSS file to use relative paths
- css["href"] = f"./{file_name}"
- else:
- raise Exception(
- f"Failed to download CSS file: {res.status_code} {res.reason}")
- for js in js_scripts:
- if js.get("src"):
- js_url = js.get("src")
- logger.debug(f"Downloading JS file: {js_url}")
- res = requests.get(js_url)
- if res.ok:
- file_name = js_url.split("/")[-1]
- # Download JS file
- (new_dir / "public" / file_name).write_text(res.text)
- # Rewrite JS file to use relative paths
- js["src"] = f"./{file_name}"
- else:
- raise Exception(
- f"Failed to download JS file: {res.status_code} {res.reason}")
- sim_html = soup.prettify(formatter="html5")
- (new_dir / "public" / "---simulator.html").write_text(sim_html)
- # Copy or download icon to favicon.ico in public directory
- if config.icon:
- logger.debug(f"Found icon to use")
- if config.icon_source_type == IconSourceType.URL:
- logger.debug(f"Downloading icon from {config.icon}")
- res = requests.get(config.icon)
- buffer = BytesIO(res.content)
- if res.ok:
- im = Image.open(buffer)
- else:
- raise Exception(
- f"Failed to download icon: {res.status_code} {res.reason}")
- else:
- logger.debug(f"Reading icon from {config.icon}")
- im = Image.open(config.icon)
- logger.debug("Saving icon as favicon.ico")
- im.save(new_dir / "public" / "favicon.ico")
diff --git a/src/convert/website_to_electron/electron.py b/src/convert/website_to_electron/electron.py
deleted file mode 100644
index 34d4e50..0000000
--- a/src/convert/website_to_electron/electron.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import json
-import logging
-import shutil
-from io import BytesIO
-from pathlib import Path
-from typing import Callable
-
-import requests
-from PIL import Image
-
-from convert.mkcd_to_website.config import Config, IconSourceType, SourceType
-from utils.cmd import run_shell_command
-from utils.filesystem import copy_these, delete_these
-from utils.logger import create_logger
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-
-def get_icon(config: Config, src_dir: Path):
- """
- Prepares the icon for the Electron app. If the icon is a URL, it downloads the icon
- and saves it to the specified path. If the icon is a local file, it copies the file
- to the specified path. Then it will convert it to ICO for Windows, ICNS for Mac, and
- PNG for Linux.
-
- :param config: The configuration object containing the project information.
- :param src_dir: The src directory of the Electron app - the icon will be saved here.
- """
- if config.icon is None:
- logger.debug("No icon specified, skipping icon generation.")
- return
- logger.debug(f"Preparing icon")
- icon_dir = src_dir / "assets" / "icons"
- icon_dir.mkdir(parents=True, exist_ok=True)
- if config.icon_source_type == IconSourceType.PATH:
- path = Path(config.icon)
- source_icon_path = icon_dir / f"source{path.suffix}"
- logger.debug(f"Copying icon from {path} to {source_icon_path}")
- shutil.copy(path, source_icon_path)
- else:
- url = config.icon
- logger.debug(f"Downloading icon from {url}")
- res = requests.get(url)
- if not res.ok:
- logger.error(f"Failed to download icon from {url}")
- raise Exception(f"Failed to download icon from {url}")
- buffer = BytesIO(res.content)
- logger.debug(
- f"Downloaded image size {round(buffer.getbuffer().nbytes / 1024)} kb")
- img = Image.open(buffer)
- source_icon_path = icon_dir / f"source.png"
- img.save(source_icon_path)
- logger.debug(f"Saved icon to {source_icon_path}")
-
- formats = ["ico", "icns", "png"]
- source_img = Image.open(source_icon_path)
- for format in formats:
- icon_path = icon_dir / f"icon.{format}"
- logger.debug(f"Converting icon to {format} and saving to {icon_path}")
- source_img.save(icon_path, format=format.upper(),
- sizes=[(256, 256)] if format == "ico" else None)
-
-
-def generate_electron(config: Config, prj_name: str, template_dir: Path, dist_dir: Path,
- cwd: Path):
- """
- Generate the Electron app from static HTML, CSS, and JS files. Assumes index.html
- is the entry point.
-
- :param config: The configuration object containing the project information.
- :param prj_name: The name of the project.
- :param template_dir: The directory containing the template files.
- :param dist_dir: The dist directory with all the static HTML, CSS, and JS files.
- :param cwd: The current working directory where the project will be created.
- """
- logger.debug(f"Creating Electron app for {prj_name}")
- # Initialize an Electron project
- prj_dir = cwd / prj_name
- prj_src_dir = prj_dir / "src"
- if prj_dir.exists():
- logger.debug(f"Project {prj_name} already exists, continuing...")
- else:
- run_shell_command(
- f"npx --yes create-electron-app@latest {prj_name} --template=webpack",
- cwd=cwd)
- delete_these(["package-lock.json"], prj_dir)
- delete_these(["index.html", "index.css"], prj_src_dir)
- # Start copying files from template
- old_dir = template_dir
- new_dir = prj_dir
-
- def copy_template(file_name: str, callback: Callable[[str], str] = lambda x: x):
- (new_dir / file_name).write_text(callback((old_dir / file_name).read_text()))
-
- logger.debug(f"Copying website files from {old_dir} to {new_dir}")
- # Modify package.json
- package_json = json.loads((old_dir / "package.json").read_text())
- package_json["name"] = prj_name
- package_json["productName"] = config.title
- package_json["version"] = config.version
- package_json["description"] = config.description
- package_json["author"] = config.author
- (new_dir / "package.json").write_text(json.dumps(package_json, indent=2))
- # Copy README.md
- copy_template("README.md",
- lambda x: x.format(WEBSITE_NAME=prj_name,
- SOURCE=f"{config.source} @ {config.source_checkout}" if config.source_type == SourceType.GITHUB else config.source))
- # Copy forge.config.js, webpack.main.config.js, etc.
- for file_name in ("forge.config.js", "webpack.main.config.js",
- "webpack.renderer.config.js", "webpack.rules.js"):
- copy_template(file_name)
- # Copy src directory
- copy_these(list([p.name for p in (old_dir / "src").glob("*")]), old_dir / "src",
- prj_dir / "src")
- # Copy dist directory
- static_dir = prj_src_dir / "static"
- static_dir.mkdir(parents=True, exist_ok=True)
- copy_these(list([p.name for p in dist_dir.glob("*")]), dist_dir, static_dir)
- # Get icons
- get_icon(config, prj_src_dir)
- # yarn
- run_shell_command("yarn", cwd=new_dir)
diff --git a/src/convert/website_to_tauri/tauri.py b/src/convert/website_to_tauri/tauri.py
deleted file mode 100644
index 78af445..0000000
--- a/src/convert/website_to_tauri/tauri.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import json
-import logging
-import shutil
-from io import BytesIO
-from pathlib import Path
-from typing import Callable
-
-import requests
-from PIL import Image
-
-from convert.mkcd_to_website.config import Config, IconSourceType, SourceType
-from utils.cmd import run_shell_command
-from utils.filesystem import copy_these, delete_these
-from utils.logger import create_logger
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-
-def get_icon(config: Config, src_dir: Path):
- """
- Prepares the icon for the Electron app. If the icon is a URL, it downloads the icon
- and saves it to the specified path. If the icon is a local file, it copies the file
- to the specified path. Then it will convert it to ICO for Windows, ICNS for Mac, and
- PNG for Linux.
-
- :param config: The configuration object containing the project information.
- :param src_dir: The src-tauri directory of the Tauri app - the icon will be saved
- here.
- """
- if config.icon is None:
- logger.debug("No icon specified, skipping icon generation.")
- return
- logger.debug(f"Preparing icon")
- icon_dir = src_dir / "icons"
- icon_dir.mkdir(parents=True, exist_ok=True)
- if config.icon_source_type == IconSourceType.PATH:
- path = Path(config.icon)
- source_icon_path = icon_dir / f"source{path.suffix}"
- logger.debug(f"Copying icon from {path} to {source_icon_path}")
- shutil.copy(path, source_icon_path)
- else:
- url = config.icon
- logger.debug(f"Downloading icon from {url}")
- res = requests.get(url)
- if not res.ok:
- logger.error(f"Failed to download icon from {url}")
- raise Exception(f"Failed to download icon from {url}")
- buffer = BytesIO(res.content)
- logger.debug(
- f"Downloaded image size {round(buffer.getbuffer().nbytes / 1024)} kb")
- img = Image.open(buffer)
- source_icon_path = icon_dir / f"source.png"
- img.save(source_icon_path)
- logger.debug(f"Saved icon to {source_icon_path}")
-
- formats = ["ico", "icns", "png"]
- source_img = Image.open(source_icon_path)
- for format in formats:
- icon_path = icon_dir / f"icon.{format}"
- logger.debug(f"Converting icon to {format} and saving to {icon_path}")
- source_img.save(icon_path, format=format.upper(),
- sizes=[(256, 256)] if format == "ico" else None)
-
-
-def generate_tauri(config: Config, prj_name: str, template_dir: Path, dist_dir: Path,
- cwd: Path):
- """
- Generate the Tauri app from static HTML, CSS, and JS files.
-
- :param config: The configuration object containing the project information.
- :param prj_name: The name of the project.
- :param template_dir: The directory containing the template files.
- :param dist_dir: The dist directory with all the static HTML, CSS, and JS files.
- :param cwd: The current working directory where the project will be created.
- """
- logger.debug(f"Creating Tauri app for {prj_name}")
- # Initialize a Tauri project
- prj_dir = cwd / prj_name
- prj_src_dir = prj_dir / "src"
- if prj_dir.exists():
- logger.debug(f"Project {prj_name} already exists, continuing...")
- else:
- run_shell_command(
- f"yarn create tauri-app {prj_name} -m yarn -t vanilla -y",
- cwd=cwd)
- delete_these([".vscode"], prj_dir)
- delete_these(["assets", "index.html", "main.js", "style.css"], prj_src_dir)
- # Start copying files from template
- old_dir = template_dir
- new_dir = prj_dir
-
- def copy_template(file_name: str, callback: Callable[[str], str] = lambda x: x):
- (new_dir / file_name).write_text(callback((old_dir / file_name).read_text()))
-
- logger.debug(f"Copying website files from {old_dir} to {new_dir}")
- # Modify package.json
- package_json = json.loads((old_dir / "package.json").read_text())
- package_json["name"] = prj_name
- package_json["version"] = config.version
- package_json["description"] = config.description
- package_json["author"] = config.author
- (new_dir / "package.json").write_text(json.dumps(package_json, indent=2))
- # Copy README.md
- copy_template("README.md",
- lambda x: x.format(WEBSITE_NAME=prj_name,
- SOURCE=f"{config.source} @ {config.source_checkout}" if config.source_type == SourceType.GITHUB else config.source))
- # Copy src-tauri directory
- copy_these(list([p.name for p in (old_dir / "src-tauri").glob("*")]),
- old_dir / "src-tauri",
- prj_dir / "src-tauri")
- # Modify src-tauri/tauri.conf.json
- tauri_conf_json = json.loads((old_dir / "src-tauri/tauri.conf.json").read_text())
- tauri_conf_json["productName"] = config.title
- tauri_conf_json["version"] = config.version
- tauri_conf_json["identifier"] = f"com.{prj_name}.app"
- tauri_conf_json["app"]["windows"][0]["title"] = config.title
- tauri_conf_json["app"]["windows"][0]["width"] = 160 * 4
- tauri_conf_json["app"]["windows"][0]["height"] = 120 * 4
- (new_dir / "src-tauri" / "tauri.conf.json").write_text(
- json.dumps(tauri_conf_json, indent=2))
- # Copy dist directory
- prj_src_dir.mkdir(parents=True, exist_ok=True)
- copy_these(list([p.name for p in dist_dir.glob("*")]), dist_dir, prj_src_dir)
- # Get icons
- get_icon(config, prj_dir / "src-tauri")
- # yarn
- run_shell_command("yarn", cwd=prj_dir)
diff --git a/src/main.py b/src/main.py
deleted file mode 100644
index f623106..0000000
--- a/src/main.py
+++ /dev/null
@@ -1,178 +0,0 @@
-import logging
-from argparse import ArgumentParser
-from pathlib import Path
-
-from convert.mkcd_to_website.config import OutputType, parse_config
-from convert.mkcd_to_website.source import download_source
-from convert.mkcd_to_website.website import generate_website
-from convert.website_to_electron.electron import generate_electron
-from convert.website_to_tauri.tauri import generate_tauri
-from utils.cmd import run_shell_command
-from utils.filesystem import delete_these
-from utils.logger import create_logger, set_all_stdout_logger_levels
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-parser = ArgumentParser(description="Convert your MakeCode Arcade games into a "
- "standalone offline executable!")
-parser.add_argument("config_path", type=Path,
- help="Path to the YAML configuration file.")
-parser.add_argument("--no-cache", action="store_true",
- help="Do not use cached files. This will delete and download all "
- "necessary files.")
-parser.add_argument("--skip-source-download", action="store_true",
- help="Skip source code download. This is useful for debugging.")
-parser.add_argument("--skip-bin-build", action="store_true",
- help="Skip building the game binary. This is useful for debugging.")
-parser.add_argument("--skip-website-gen", action="store_true",
- help="Skip website generation. This is useful for debugging.")
-parser.add_argument("--skip-website-build", action="store_true",
- help="Skip building the website. This is useful for debugging.")
-parser.add_argument("--skip-electron-gen", action="store_true",
- help="Skip Electron app generation. This is useful for debugging.")
-parser.add_argument("--skip-electron-build", action="store_true",
- help="Skip building the Electron app. This is useful for debugging.")
-parser.add_argument("--skip-tauri-gen", action="store_true",
- help="Skip Tauri app generation. This is useful for debugging.")
-parser.add_argument("--skip-tauri-build", action="store_true",
- help="Skip building the Tauri app. This is useful for debugging.")
-parser.add_argument("--debug", action="store_true",
- help="Enable debug logging.")
-args = parser.parse_args()
-debug = bool(args.debug)
-if debug:
- set_all_stdout_logger_levels(logging.DEBUG)
-logger.debug(f"Received arguments: {args}")
-
-config_path = Path(args.config_path)
-logger.info(f"Loading configuration from {config_path}")
-config = parse_config(config_path.read_text(), config_path.parent)
-
-output_format = config.output
-logger.debug(f"Building to {output_format.value}")
-logger.debug(f"Window title will be {config.title}")
-
-no_cache = bool(args.no_cache)
-if no_cache:
- logger.info("No cache option selected. Ignoring cached files.")
-skip_source_download = bool(args.skip_source_download)
-skip_bin_build = bool(args.skip_bin_build)
-skip_website_gen = bool(args.skip_website_gen)
-skip_website_build = bool(args.skip_website_build)
-skip_electron_gen = bool(args.skip_electron_gen)
-skip_electron_build = bool(args.skip_electron_build)
-skip_tauri_gen = bool(args.skip_tauri_gen)
-skip_tauri_build = bool(args.skip_tauri_build)
-
-cwd = config_path.parent / config.name
-src_dir = Path(__file__).parent
-logger.debug(f"Current working directory: {cwd} (source code directory will be "
- f"downloaded here)")
-logger.debug(f"Source code directory: {src_dir}")
-cwd.mkdir(parents=True, exist_ok=True)
-
-# Download source code
-if skip_source_download:
- logger.info("Skipping source code download")
- source_code_path = cwd / f"{config.name} source"
-else:
- logger.info("Downloading source code")
- source_code_path = download_source(config, cwd, no_cache)
-
-# npx pxt build
-binary_js_path = source_code_path / "built" / "binary.js"
-if skip_bin_build:
- logger.info("Skipping build")
-else:
- logger.info("Building project")
- if no_cache:
- logger.debug("Checking for binary to remove")
- if binary_js_path.exists():
- logger.debug(f"Deleting {binary_js_path}")
- binary_js_path.unlink()
- run_shell_command("npx mkc build -j", cwd=source_code_path)
-logger.debug(f"Binary JS path: {binary_js_path}")
-
-# yarn create vite, copy files, and substitute values
-vite_project_name = f"{config.name.lower().replace(" ", "-")}-website"
-website_path = cwd / vite_project_name
-if skip_website_gen:
- logger.info("Skipping website generation")
-else:
- logger.info(f"Generating TS React and Vite website")
- if no_cache:
- logger.debug("Checking for existing website to remove")
- delete_these([vite_project_name], cwd)
- logger.debug(f"Creating Vite project with name {vite_project_name}")
- generate_website(config, vite_project_name, src_dir / "templates" / "website_files",
- cwd, binary_js_path)
-
-# yarn run build
-website_dist_path = website_path / "dist"
-if skip_website_build:
- logger.info("Skipping website build")
-else:
- logger.info("Building website")
- run_shell_command("yarn build", cwd=website_path)
-
-logger.info(f"Static website files are at {website_dist_path}")
-if output_format == OutputType.STATIC:
- logger.info(f"Build finished")
- exit(0)
-elif output_format == OutputType.ELECTRON:
- electron_project_name = f"{config.name.lower().replace(" ", "-")}-electron"
- electron_path = cwd / electron_project_name
- if skip_electron_gen:
- logger.info("Skipping Electron app generation")
- else:
- logger.info(f"Generating Electron app")
- logger.debug(
- f"Creating Electron app in {cwd}, using {website_dist_path} for source")
- logger.debug(f"Creating Electron project with name {electron_project_name}")
- # npx create-electron-app@latest, copy files, and substitute values
- if no_cache:
- logger.debug("Checking for existing website to remove")
- delete_these([electron_project_name], cwd)
- generate_electron(config, electron_project_name,
- src_dir / "templates" / "electron_files", website_dist_path,
- cwd)
-
- # yarn run make
- electron_dist_path = electron_path / "out"
- if skip_electron_build:
- logger.info("Skipping Electron app build")
- else:
- logger.info("Building Electron app")
- run_shell_command("yarn run make", cwd=electron_path)
-
- logger.info(f"Electron app executables are at {electron_dist_path}")
- logger.info(f"Build finished")
- exit(0)
-elif output_format == OutputType.TAURI:
- tauri_project_name = f"{config.name.lower().replace(' ', '-')}-tauri"
- tauri_path = cwd / tauri_project_name
- if skip_tauri_gen:
- logger.info("Skipping Tauri app generation")
- else:
- logger.info(f"Generating Tauri app")
- logger.debug(
- f"Creating Tauri app in {cwd}, using {website_dist_path} for source")
- logger.debug(f"Creating Tauri project with name {tauri_project_name}")
- # yarn create tauri-app
- if no_cache:
- logger.debug("Checking for existing website to remove")
- delete_these([tauri_project_name], cwd)
- generate_tauri(config, tauri_project_name,
- src_dir / "templates" / "tauri_files", website_dist_path, cwd)
-
- # yarn run tauri build
- tauri_dist_path = tauri_path / "src-tauri" / "target" / "release"
- if skip_tauri_build:
- logger.info("Skipping Tauri app build")
- else:
- logger.info("Building Tauri app")
- run_shell_command("yarn run tauri build", cwd=tauri_path)
-
- logger.info(f"Tauri app executables are at {tauri_dist_path}")
- logger.info(f"Build finished")
- exit(0)
diff --git a/src/convert/__init__.py b/src/mkcd2app/__init__.py
similarity index 100%
rename from src/convert/__init__.py
rename to src/mkcd2app/__init__.py
diff --git a/src/mkcd2app/build_project/__init__.py b/src/mkcd2app/build_project/__init__.py
new file mode 100644
index 0000000..da676b7
--- /dev/null
+++ b/src/mkcd2app/build_project/__init__.py
@@ -0,0 +1,80 @@
+import logging
+import shutil
+from pathlib import Path
+
+from redun import task
+from redun.file import ContentDir
+
+from mkcd2app.build_project.inputs.code import build_binary_js, \
+ download_supporting_files, fetch_code
+from mkcd2app.build_project.website import copy_website_template, \
+ fill_website_template, install_website_dependencies
+from mkcd2app.config import load_config_from_yaml
+from mkcd2app.utils.logger import create_logger
+from mkcd2app.utils.resources import get_js_tools_path
+from mkcd2app.utils.run import run_cmd
+
+logger = create_logger(name=__name__, level=logging.INFO)
+
+
+@task(namespace="mkcd2app")
+def install_mkcd_build_tools(config_yaml: str) -> ContentDir:
+ """
+ Installs the MakeCode Arcade build tools.
+
+ :param config_yaml: The raw YAML text of the config file.
+ :return: A redun.ContentDir that points to node_modules, this is only used so that
+ redun will see that some tasks depend on `mkc` being installed.
+ """
+ logger.info("Installing MakeCode Arcade build tools")
+
+ config = load_config_from_yaml(config_yaml)
+ build_path = Path(config.build_dir)
+ logger.debug(f"Tools will be installed in {build_path}")
+
+ with get_js_tools_path() as js_tools_path:
+ shutil.copy(js_tools_path / "package.json", build_path / "package.json")
+ shutil.copy(js_tools_path / "package-lock.json",
+ build_path / "package-lock.json")
+
+ run_cmd("npm ci", cwd=build_path)
+
+ logger.debug("All MakeCode Arcade build tools installed")
+
+ return ContentDir(str(build_path / "node_modules"))
+
+
+@task(namespace="mkcd2app")
+def build_project(config_yaml: str):
+ """
+ Build the project.
+
+ :param config_yaml: The raw YAML text of the config file.
+ """
+ logger.info("Building project")
+
+ config = load_config_from_yaml(config_yaml)
+ build_dir = Path(config.build_dir)
+ logger.debug(f"Project build directory is at {build_dir}")
+ build_dir.mkdir(parents=True, exist_ok=True)
+
+ # Install `mkc` with `npm ci` in build dir
+ node_modules_for_mkc = install_mkcd_build_tools(config_yaml)
+ # Fetch game source code with `mkc`, `git`, or copy from disk
+ code_path = fetch_code(config_yaml, node_modules_for_mkc)
+ # Build binary.js with `mkc`
+ bin_js_path = build_binary_js(config_yaml, code_path)
+ # Download supporting files to run binary.js, including ---simulator.html and all
+ # it's references
+ support_path = download_supporting_files(config_yaml)
+
+ # Copy website template (clean copy with template files only)
+ website_path = copy_website_template(config_yaml)
+ # Copy + fill (separate dir so stages don't mutate each other's
+ # ContentDir/ContentPath
+ website_filled_path = fill_website_template(config_yaml, website_path, bin_js_path,
+ support_path)
+ # Copy + `npm ci` (separate dir for same reason)
+ website_filled_deps_path = install_website_dependencies(website_filled_path)
+
+ return website_filled_deps_path
diff --git a/src/convert/mkcd_to_website/__init__.py b/src/mkcd2app/build_project/inputs/__init__.py
similarity index 100%
rename from src/convert/mkcd_to_website/__init__.py
rename to src/mkcd2app/build_project/inputs/__init__.py
diff --git a/src/mkcd2app/build_project/inputs/code.py b/src/mkcd2app/build_project/inputs/code.py
new file mode 100644
index 0000000..a8e3950
--- /dev/null
+++ b/src/mkcd2app/build_project/inputs/code.py
@@ -0,0 +1,169 @@
+import logging
+import shutil
+from io import BytesIO
+from pathlib import Path
+
+import requests
+from PIL import Image
+from bs4 import BeautifulSoup
+from redun import task
+from redun.file import ContentDir, ContentFile
+
+from mkcd2app.config import load_config_from_yaml
+from mkcd2app.config.model import GitHubCodeSource, PathAssetSource, PathCodeSource, \
+ ShareLinkCodeSource, \
+ UrlAssetSource
+from mkcd2app.utils.logger import create_logger
+from mkcd2app.utils.run import run_cmd
+
+logger = create_logger(name=__name__, level=logging.INFO)
+
+
+@task(namespace="mkcd2app")
+def fetch_code(config_yaml: str, node_modules_for_mkc: ContentDir) -> ContentDir:
+ """
+ Download/clone/copy the source code to the build directory.
+
+ Example input: config_yaml, ./node_modules
+ Example output: ./racers-source
+
+ :param config_yaml: The raw YAML text of the config file.
+ :param node_modules_for_mkc: redun.ContentDir that points to the node_modules
+ directory, this ensures that this task depends on `mkc` being installed.
+ :return: A redun.ContentDir that points to the source code.
+ """
+ config = load_config_from_yaml(config_yaml)
+ code_path = Path(config.build_dir) / f"{config.project.path_friendly_name}-source"
+ logger.info(f"MakeCode Arcade source will be downloaded/copied to {code_path}")
+
+ # Clean previous output, not wasteful because redun already checks our inputs
+ # and outputs to see if they changed
+ if code_path.exists():
+ shutil.rmtree(code_path)
+
+ # TODO: Implement source code download from GitHub
+ # TODO: Implement source code copy
+ match config.inputs.code.root:
+ case ShareLinkCodeSource(value=url):
+ logger.debug(f"Downloading source code from {url}")
+ code_path.mkdir(parents=True)
+ logger.debug(f"Using `mkc` from {node_modules_for_mkc}")
+ run_cmd(f"npx mkc download {url}", cwd=code_path)
+ case GitHubCodeSource(value=url, checkout=checkout_target):
+ logger.debug(f"Downloading source code from {url}@{checkout_target}")
+ case PathCodeSource(value=path):
+ logger.debug(f"Copying source code from {path}")
+
+ logger.debug("Source code downloaded")
+ return ContentDir(str(code_path))
+
+
+@task(namespace="mkcd2app")
+def build_binary_js(config_yaml: str, code_path: ContentDir) -> ContentFile:
+ """
+ Build the MakeCode Arcade binary.js file
+
+ Example input: config_yaml, ./racers-source
+ Example output: ./racers-binary-js/binary.js
+
+ :param config_yaml: The raw YAML text of the config file.
+ :param code_path: A redun.ContentDir where the code is stored.
+ :return: A redun.ContentFile that points to binary.js
+ """
+ config = load_config_from_yaml(config_yaml)
+
+ logger.info("Building MakeCode Arcade binary.js")
+
+ cwd = Path(code_path.path)
+ logger.debug(f"Building in cwd {cwd}")
+ run_cmd("npx mkc build -j", cwd=cwd)
+
+ bin_js_path = cwd / "built" / "binary.js"
+ logger.debug(f"binary.js available at {bin_js_path}")
+
+ # Copy binary to a stable location outside code_dir so we don't
+ # pollute fetch_code's ContentDir hash on subsequent runs.
+ output_path = cwd.parent / f"{config.project.path_friendly_name}-binary-js" / "binary.js"
+ output_path.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copy(bin_js_path, output_path)
+ shutil.rmtree(cwd / "built")
+
+ return ContentFile(str(output_path))
+
+
+@task(namespace="mkcd2app")
+def download_supporting_files(config_yaml: str) -> ContentDir:
+ """
+ Download all supporting files needed to run binary.js for the website
+
+ Example input: config_yaml
+ Example output: ./racers-binary-js-support
+
+ :param config_yaml: The raw YAML text of the config file.
+ :return: redun.ContentDir that points to a directory of files that should be copied
+ to the website's public folder as is alongside the binary.js file.
+ """
+ config = load_config_from_yaml(config_yaml)
+ support_path = Path(
+ config.build_dir) / f"{config.project.path_friendly_name}-binary-js-support"
+ logger.info(f"Downloading supporting files to {support_path}")
+
+ # Clean previous output, not wasteful because redun handles caching
+ if support_path.exists():
+ shutil.rmtree(support_path)
+ support_path.mkdir(parents=True)
+
+ logger.debug(f"Downloading main simulator file")
+ res = requests.get("https://trg-arcade.userpxt.io/---simulator")
+ res.raise_for_status()
+ sim_html = res.text
+
+ logger.debug(f"Analyzing sim HTML ({len(sim_html)} chars) for required CSS and JS "
+ f"files")
+ soup = BeautifulSoup(sim_html, features="html.parser")
+ css_links = soup.find_all("link", rel="stylesheet")
+ js_scripts = soup.find_all("script")
+ logger.debug(f"Found {len(css_links)} CSS links and {len(js_scripts)} JS scripts")
+ for css in css_links:
+ url = css.get("href")
+ if url:
+ logger.debug(f"Downloading CSS file {url}")
+ res = requests.get(url)
+ res.raise_for_status()
+ file_name = url.split("/")[-1]
+ path = support_path / file_name
+ path.write_text(res.text)
+ css["href"] = f"./{file_name}"
+ logger.debug(f"Wrote to {path}")
+ for js in js_scripts:
+ url = js.get("src")
+ if url:
+ logger.debug(f"Downloading JS file {url}")
+ res = requests.get(url)
+ res.raise_for_status()
+ file_name = url.split("/")[-1]
+ path = support_path / file_name
+ path.write_text(res.text)
+ js["src"] = f"./{file_name}"
+ logger.debug(f"Wrote to {path}")
+ new_sim_html = soup.prettify(formatter="html5")
+ path = support_path / "---simulator.html"
+ path.write_text(new_sim_html)
+ logger.debug(f"Wrote modified simulator HTML to {path}")
+
+ if config.inputs.assets.icon:
+ match config.inputs.assets.icon.root:
+ case UrlAssetSource(value=url):
+ logger.debug(f"Downloading icon from {url}")
+ res = requests.get(str(url))
+ res.raise_for_status()
+ buffer = BytesIO(res.content)
+ im = Image.open(buffer)
+ case PathAssetSource(value=path):
+ logger.debug(f"Opening icon from {path}")
+ im = Image.open(path)
+ favicon_path = support_path / "favicon.ico"
+ logger.debug(f"Saving favicon to {favicon_path}")
+ im.save(favicon_path)
+
+ return ContentDir(str(support_path))
diff --git a/src/mkcd2app/build_project/website.py b/src/mkcd2app/build_project/website.py
new file mode 100644
index 0000000..cf84029
--- /dev/null
+++ b/src/mkcd2app/build_project/website.py
@@ -0,0 +1,119 @@
+import logging
+import shutil
+from pathlib import Path
+
+from redun import task
+from redun.file import ContentDir, ContentFile
+
+from mkcd2app.config import load_config_from_yaml
+from mkcd2app.utils.logger import create_logger
+from mkcd2app.utils.resources import get_template_path
+from mkcd2app.utils.run import run_cmd
+
+logger = create_logger(name=__name__, level=logging.INFO)
+
+
+def _website_dir(build_dir: Path, name: str, suffix: str) -> Path:
+ """Return a directory path like ``build_dir/{name}-website{suffix}``."""
+ return build_dir / f"{name}-website{suffix}"
+
+
+@task(namespace="mkcd2app")
+def copy_website_template(config_yaml: str) -> ContentDir:
+ """
+ Copy the template to the build directory.
+
+ Example input: config_yaml
+ Example output: racers-website
+
+ :param config_yaml: The raw YAML text of the config file.
+ :return: A redun.ContentDir that points to the Vite website template directory.
+ """
+ config = load_config_from_yaml(config_yaml)
+ dst = _website_dir(Path(config.build_dir), config.project.path_friendly_name, "")
+ logger.info(f"Website will be copied to {dst}")
+ if dst.exists():
+ shutil.rmtree(dst)
+ with get_template_path("vite-project") as src:
+ logger.debug(f"Copying template from {src}")
+ shutil.copytree(src, dst)
+ logger.debug("Website template copied")
+ return ContentDir(str(dst))
+
+
+@task(namespace="mkcd2app")
+def fill_website_template(config_yaml: str,
+ website_path: ContentDir,
+ bin_js_path: ContentFile,
+ support_path: ContentDir) -> ContentDir:
+ """
+ "Fill" the website template — copy binary.js and support files into ``public/``
+ and update the ``
``.
+
+ We copy the deps-installed website first so we never mutate a ContentDir
+ that another task owns.
+
+ Example intput: racers-website
+ Example output: racers-website-filled
+
+ :param config_yaml: The raw YAML text of the config file.
+ :param website_path: redun.ContentDir pointing to the deps-installed website.
+ :param bin_js_path: redun.ContentFile pointing to the binary JS file.
+ :param support_path: redun.ContentDir pointing to the support file directory.
+ :return: A redun.ContentDir pointing to the filled website directory.
+ """
+ logger.info("Filling website template")
+
+ config = load_config_from_yaml(config_yaml)
+ src = Path(website_path.path)
+ dst = src.parent / f"{src.name}-filled"
+ if dst.exists():
+ shutil.rmtree(dst)
+ shutil.copytree(src, dst)
+
+ public_path = dst / "public"
+ logger.debug(f"public directory is at {public_path}")
+
+ logger.debug(f"Copying binary.js from {bin_js_path.path}")
+ shutil.copy(bin_js_path.path, public_path)
+
+ logger.debug(f"Copying support files from {support_path.path}")
+ shutil.copytree(support_path.path, public_path, dirs_exist_ok=True)
+
+ title = config.project.title.format(NAME=config.project.name,
+ VERSION=config.project.version,
+ AUTHOR=config.project.author)
+ logger.debug(f"Using title \"{title}\" in index.html")
+ index_html_path = dst / "index.html"
+ index_html_text = index_html_path.read_text()
+ index_html_text = index_html_text.replace("vite-project",
+ f"{title}")
+ index_html_path.write_text(index_html_text)
+
+ logger.debug("Website template filled")
+ return ContentDir(str(dst))
+
+
+@task(namespace="mkcd2app")
+def install_website_dependencies(website_filled_path: ContentDir) -> ContentDir:
+ """
+ Make a copy of the template website, then ``npm ci`` in it.
+
+ We copy so that we never mutate a ContentDir that another task owns,
+ keeping ContentDir hashes stable for caching.
+
+ Example input: racers-website-filled
+ Example output: racers-website-filled-deps
+
+ :param website_filled_path: A redun.ContentDir pointing to the template website.
+ :return: A redun.ContentDir pointing to the deps-installed website copy.
+ """
+ src = Path(website_filled_path.path)
+ dst = src.parent / f"{src.name}-deps"
+ logger.info(f"Installing website dependencies — copy {src} -> {dst}")
+ if dst.exists():
+ shutil.rmtree(dst)
+ shutil.copytree(src, dst)
+ run_cmd("npm ci", cwd=dst)
+ logger.debug("Website dependencies installed")
+ return ContentDir(str(dst))
diff --git a/src/mkcd2app/config/__init__.py b/src/mkcd2app/config/__init__.py
new file mode 100644
index 0000000..8f1d4c4
--- /dev/null
+++ b/src/mkcd2app/config/__init__.py
@@ -0,0 +1,27 @@
+import logging
+
+import yaml
+
+from mkcd2app.config.model import BuildConfig
+from mkcd2app.utils.logger import create_logger
+
+logger = create_logger(name=__name__, level=logging.INFO)
+
+
+def load_config_from_yaml(config_text: str) -> BuildConfig:
+ """
+ Load a config from YAML text.
+
+ :param config_text: The text of the config file, not the path.
+ :return: A Config object.
+ """
+ logger.debug(f"Loading config from {len(config_text)} characters of YAML text")
+ data = yaml.safe_load(config_text)
+
+ if data["version"] == 1: # only one config version so far
+ config = BuildConfig(**data)
+ else:
+ raise NotImplementedError(f"Unknown config version {data.version}")
+ logger.debug(f"Loaded config version {config.version}")
+
+ return config
diff --git a/src/mkcd2app/config/model.py b/src/mkcd2app/config/model.py
new file mode 100644
index 0000000..3d64850
--- /dev/null
+++ b/src/mkcd2app/config/model.py
@@ -0,0 +1,113 @@
+from typing import Annotated, List, Literal, Optional, Union
+
+from pydantic import BaseModel, Field, HttpUrl, RootModel
+
+
+# Source code can be a share link, GitHub repo, or a path on disk
+
+class ShareLinkCodeSource(BaseModel):
+ type: Literal["share_link"]
+ value: HttpUrl
+
+
+class GitHubCodeSource(BaseModel):
+ type: Literal["github"]
+ value: HttpUrl
+ checkout: str = "master"
+
+
+class PathCodeSource(BaseModel):
+ type: Literal["path"]
+ value: str
+
+
+CodeSource = RootModel[
+ Annotated[
+ Union[ShareLinkCodeSource, GitHubCodeSource, PathCodeSource],
+ Field(discriminator="type")
+ ]
+]
+
+
+# Assets can be a URL or path on disk
+
+class UrlAssetSource(BaseModel):
+ type: Literal["url"]
+ value: HttpUrl
+
+
+class PathAssetSource(BaseModel):
+ type: Literal["path"]
+ value: str
+
+
+AssetSource = RootModel[
+ Annotated[
+ Union[UrlAssetSource, PathAssetSource],
+ Field(discriminator="type")
+ ]
+]
+
+
+# Right now we only have one asset so far, just the icon
+class Assets(BaseModel):
+ icon: AssetSource
+
+
+# To build a project, we need multiple inputs
+class Inputs(BaseModel):
+ code: CodeSource
+ assets: Assets
+
+
+# Project metadata
+class Project(BaseModel):
+ name: str
+ path_friendly_name: str
+ description: Optional[str] = None
+ author: str
+ version: str
+ title: str = "{NAME} v{VERSION}"
+
+
+class WindowConfig(BaseModel):
+ width: int = 640
+ height: int = 480
+
+
+# Different types of outputs available that we can build
+
+
+class StaticOutput(BaseModel):
+ type: Literal["static"]
+
+
+class ElectronOutput(BaseModel):
+ type: Literal["electron"]
+ window: Optional[WindowConfig] = None
+
+
+class TauriOutput(BaseModel):
+ type: Literal["tauri"]
+ identifier: str
+ window: Optional[WindowConfig] = None
+
+
+OutputOption = RootModel[
+ Annotated[
+ Union[StaticOutput, ElectronOutput, TauriOutput],
+ Field(discriminator="type")
+ ]
+]
+
+
+# The entire config
+class BuildConfig(BaseModel):
+ version: int # currently only 1 version so far
+ project: Project
+ inputs: Inputs
+ build_dir: str = Field(..., alias="build_dir")
+ outputs: List[OutputOption]
+
+ class Config:
+ populate_by_name = True
diff --git a/src/mkcd2app/js_tools/package-lock.json b/src/mkcd2app/js_tools/package-lock.json
new file mode 100644
index 0000000..e36cfd3
--- /dev/null
+++ b/src/mkcd2app/js_tools/package-lock.json
@@ -0,0 +1,209 @@
+{
+ "name": "MakeCode-Arcade-to-App",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "makecode": "^1.3.5"
+ }
+ },
+ "node_modules/@xmldom/xmldom": {
+ "version": "0.9.10",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.6"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "8.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "license": "MIT"
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "license": "ISC"
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "license": "ISC"
+ },
+ "node_modules/makecode": {
+ "version": "1.3.5",
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "commander": "^8.2.0",
+ "glob": "^7.2.0",
+ "makecode-core": "^1.7.3",
+ "node-watch": "^0.7.2",
+ "semver": "^7.3.7"
+ },
+ "bin": {
+ "makecode": "makecode",
+ "mkc": "makecode"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/makecode-core": {
+ "version": "1.7.9",
+ "license": "MIT",
+ "dependencies": {
+ "@xmldom/xmldom": "^0.9.8",
+ "chalk": "^4.1.2"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.5",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/node-watch": {
+ "version": "0.7.4",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "license": "ISC"
+ }
+ }
+}
diff --git a/src/mkcd2app/js_tools/package.json b/src/mkcd2app/js_tools/package.json
new file mode 100644
index 0000000..3f36666
--- /dev/null
+++ b/src/mkcd2app/js_tools/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "makecode": "^1.3.5"
+ }
+}
diff --git a/src/mkcd2app/main.py b/src/mkcd2app/main.py
new file mode 100644
index 0000000..e382777
--- /dev/null
+++ b/src/mkcd2app/main.py
@@ -0,0 +1,92 @@
+import logging
+from argparse import ArgumentParser
+from pathlib import Path
+
+import redun
+import redun.file
+from redun import Scheduler
+
+# Monkey patch ContentDir to use content-based hashing instead of mtime-based hashing.
+# This addresses the issue where cache busts even on identical runs.
+# Note: ContentDir.__iter__() already yields ContentFile objects (via
+# ContentFileClasses), so f.hash is content-based — no need to create redundant
+# ContentFile instances.
+def _content_dir_calc_hash(self, files=None):
+ if files is None:
+ files = list(self)
+ from redun.hashing import hash_struct
+ return hash_struct([self.type_basename, self.path] + sorted(f.hash for f in files))
+
+redun.file.ContentDir._calc_hash = _content_dir_calc_hash
+
+# Also patch Dir._calc_hash to prevent mtime-based hashing leak through any
+# code path that uses Dir directly (e.g. FileSystem.iter_file_hashes).
+# Note: we intentionally drop the `files` param because Dir.update_hash()
+# passes File objects (mtime-based), not ContentFile objects. Re-iterating
+# via ContentDir gives properly content-based hashing.
+def _dir_calc_hash(self, files=None):
+ from redun.file import ContentDir
+ return ContentDir(self.path)._calc_hash()
+
+redun.file.Dir._calc_hash = _dir_calc_hash
+
+from mkcd2app.build_project import build_project
+from mkcd2app.config import load_config_from_yaml
+from mkcd2app.utils.logger import create_logger, set_all_stdout_logger_levels
+
+logger = create_logger(name=__name__, level=logging.INFO)
+
+
+def main():
+ parser = ArgumentParser(description="Convert your MakeCode Arcade games into a "
+ "standalone offline executable!")
+ parser.add_argument("--debug", action="store_true",
+ help="Enable debug logging. This must go first before the sub "
+ "command.")
+ subparsers = parser.add_subparsers(required=True, dest="command")
+ # build subcommand
+ parser_build = subparsers.add_parser("build",
+ help="Build your MakeCode Arcade game.")
+ parser_build.add_argument("config", type=str, help="Path to the YAML config file.")
+
+ args = parser.parse_args()
+ debug = bool(args.debug)
+ if debug:
+ set_all_stdout_logger_levels(logging.DEBUG)
+ logger.debug(f"Received arguments: {args}")
+
+ if args.command == "build":
+ logger.debug("Building project")
+
+ config_path = Path(args.config)
+ logger.debug(f"Loading config from {config_path}")
+ config_text = config_path.read_text()
+
+ # Parse once only to extract build_dir for the redun DB path.
+ # The raw YAML text is passed to redun tasks so that argument
+ # hashing is deterministic (string) rather than pickle-based
+ # (which is non-deterministic due to pydantic's set fields).
+ config = load_config_from_yaml(config_text)
+ build_dir = Path(config.build_dir)
+ build_dir.mkdir(parents=True, exist_ok=True)
+ db_uri = f"sqlite:///{build_dir.resolve() / '.redun-cache.db'}"
+ logger.debug(f"redun cache DB: {db_uri}")
+ # noinspection PyUnresolvedReferences
+ redun_config = redun.config.Config({
+ "scheduler": {"log_level": "DEBUG"},
+ "backend": {"db_uri": db_uri},
+ })
+ scheduler = Scheduler(config=redun_config)
+ # Load/migrate the backend so the persistent DB is properly set up.
+ # Without this, providing a custom db_uri skips the automatic
+ # engine creation and migration that the in-memory default does.
+ scheduler.load()
+ result = scheduler.run(
+ build_project(config_text),
+ )
+
+ logger.debug(f"Build finished, result is at {result}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/templates/tauri_files/.gitignore b/src/mkcd2app/templates/vite-project/.gitignore
similarity index 100%
rename from src/templates/tauri_files/.gitignore
rename to src/mkcd2app/templates/vite-project/.gitignore
diff --git a/src/templates/website_files/.prettierignore b/src/mkcd2app/templates/vite-project/.prettierignore
similarity index 100%
rename from src/templates/website_files/.prettierignore
rename to src/mkcd2app/templates/vite-project/.prettierignore
diff --git a/src/mkcd2app/templates/vite-project/README.md b/src/mkcd2app/templates/vite-project/README.md
new file mode 100644
index 0000000..08412ab
--- /dev/null
+++ b/src/mkcd2app/templates/vite-project/README.md
@@ -0,0 +1,75 @@
+# React + TypeScript + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
+
+## React Compiler
+
+The React Compiler is enabled on this template. See [this documentation](https://react.dev/learn/react-compiler) for more information.
+
+Note: This will impact Vite dev & build performances.
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
+
+```js
+export default defineConfig([
+ globalIgnores(["dist"]),
+ {
+ files: ["**/*.{ts,tsx}"],
+ extends: [
+ // Other configs...
+
+ // Remove tseslint.configs.recommended and replace with this
+ tseslint.configs.recommendedTypeChecked,
+ // Alternatively, use this for stricter rules
+ tseslint.configs.strictTypeChecked,
+ // Optionally, add this for stylistic rules
+ tseslint.configs.stylisticTypeChecked,
+
+ // Other configs...
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ["./tsconfig.node.json", "./tsconfig.app.json"],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+]);
+```
+
+You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
+
+```js
+// eslint.config.js
+import reactX from "eslint-plugin-react-x";
+import reactDom from "eslint-plugin-react-dom";
+
+export default defineConfig([
+ globalIgnores(["dist"]),
+ {
+ files: ["**/*.{ts,tsx}"],
+ extends: [
+ // Other configs...
+ // Enable lint rules for React
+ reactX.configs["recommended-typescript"],
+ // Enable lint rules for React DOM
+ reactDom.configs.recommended,
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ["./tsconfig.node.json", "./tsconfig.app.json"],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+]);
+```
diff --git a/src/mkcd2app/templates/vite-project/eslint.config.js b/src/mkcd2app/templates/vite-project/eslint.config.js
new file mode 100644
index 0000000..da6f026
--- /dev/null
+++ b/src/mkcd2app/templates/vite-project/eslint.config.js
@@ -0,0 +1,22 @@
+import js from "@eslint/js";
+import globals from "globals";
+import reactHooks from "eslint-plugin-react-hooks";
+import reactRefresh from "eslint-plugin-react-refresh";
+import tseslint from "typescript-eslint";
+import { defineConfig, globalIgnores } from "eslint/config";
+
+export default defineConfig([
+ globalIgnores(["dist"]),
+ {
+ files: ["**/*.{ts,tsx}"],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ globals: globals.browser,
+ },
+ },
+]);
diff --git a/src/templates/website_files/index.html b/src/mkcd2app/templates/vite-project/index.html
similarity index 75%
rename from src/templates/website_files/index.html
rename to src/mkcd2app/templates/vite-project/index.html
index 31a5cf5..a9a42a2 100644
--- a/src/templates/website_files/index.html
+++ b/src/mkcd2app/templates/vite-project/index.html
@@ -2,12 +2,12 @@
-
- {TITLE}
+
+ vite-project
-
+
diff --git a/src/mkcd2app/templates/vite-project/package-lock.json b/src/mkcd2app/templates/vite-project/package-lock.json
new file mode 100644
index 0000000..6eacf1a
--- /dev/null
+++ b/src/mkcd2app/templates/vite-project/package-lock.json
@@ -0,0 +1,3261 @@
+{
+ "name": "vite-project",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "vite-project",
+ "version": "0.0.0",
+ "dependencies": {
+ "prettier": "^3.9.5",
+ "react": "^19.2.7",
+ "react-dom": "^19.2.7",
+ "react-toastify": "^11.1.0"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.29.7",
+ "@eslint/js": "^10.0.1",
+ "@rolldown/plugin-babel": "^0.2.3",
+ "@types/babel__core": "^7.20.5",
+ "@types/node": "^24.13.2",
+ "@types/react": "^19.2.17",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.3",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "eslint": "^10.6.0",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.3",
+ "globals": "^17.7.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.62.0",
+ "vite": "^8.1.1"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz",
+ "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz",
+ "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.23.5",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+ "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^3.0.5",
+ "debug": "^4.3.1",
+ "minimatch": "^10.2.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+ "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.2.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+ "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+ "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "eslint": "^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+ "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+ "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^1.2.1",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.140.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz",
+ "integrity": "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.0.tgz",
+ "integrity": "sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.0.tgz",
+ "integrity": "sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.0.tgz",
+ "integrity": "sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.0.tgz",
+ "integrity": "sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.0.tgz",
+ "integrity": "sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.0.tgz",
+ "integrity": "sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.0.tgz",
+ "integrity": "sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.0.tgz",
+ "integrity": "sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.0.tgz",
+ "integrity": "sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.0.tgz",
+ "integrity": "sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.0.tgz",
+ "integrity": "sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.0.tgz",
+ "integrity": "sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.0.tgz",
+ "integrity": "sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.2",
+ "@emnapi/runtime": "1.11.2",
+ "@napi-rs/wasm-runtime": "^1.1.6"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.0.tgz",
+ "integrity": "sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.0.tgz",
+ "integrity": "sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/plugin-babel": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/plugin-babel/-/plugin-babel-0.2.3.tgz",
+ "integrity": "sha512-+zEk16yGlz1F9STiRr6uG9hmIXb6nprjLczV/htGptYuLoCuxb+itZ03RKCEeOhBpDDd1NU7qF6x1VLMUp62bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=22.12.0 || ^24.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.29.0 || ^8.0.0-rc.1",
+ "@babel/plugin-transform-runtime": "^7.29.0 || ^8.0.0-rc.1",
+ "@babel/runtime": "^7.27.0 || ^8.0.0-rc.1",
+ "rolldown": "^1.0.0-rc.5",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/plugin-transform-runtime": {
+ "optional": true
+ },
+ "@babel/runtime": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.13.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
+ "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.18.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
+ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz",
+ "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/type-utils": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.64.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
+ "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz",
+ "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz",
+ "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.64.0",
+ "@typescript-eslint/types": "^8.64.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz",
+ "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz",
+ "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz",
+ "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz",
+ "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz",
+ "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.64.0",
+ "@typescript-eslint/tsconfig-utils": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz",
+ "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz",
+ "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz",
+ "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/babel-plugin-react-compiler": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz",
+ "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.26.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.44",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.44.tgz",
+ "integrity": "sha512-T3ghW+sl/ZJ8w1v/yQx3qvJ9040DWoLBz8JT/CILbAKcFyG9b2MRe75v6W5uXjv6uH1lumK2Kv46y2zSkcej0Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz",
+ "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.42",
+ "caniuse-lite": "^1.0.30001803",
+ "electron-to-chromium": "^1.5.389",
+ "node-releases": "^2.0.51",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001806",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
+ "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.393",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz",
+ "integrity": "sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "10.7.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz",
+ "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==",
+ "dev": true,
+ "license": "MIT",
+ "workspaces": [
+ "packages/*"
+ ],
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.5",
+ "@eslint/config-helpers": "^0.6.0",
+ "@eslint/core": "^1.2.1",
+ "@eslint/plugin-kit": "^0.7.2",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.14.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^9.1.2",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.2.0",
+ "esquery": "^1.7.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "minimatch": "^10.2.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
+ "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz",
+ "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": "^9 || ^10"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+ "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "17.7.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
+ "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
+ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.33.0",
+ "lightningcss-darwin-arm64": "1.33.0",
+ "lightningcss-darwin-x64": "1.33.0",
+ "lightningcss-freebsd-x64": "1.33.0",
+ "lightningcss-linux-arm-gnueabihf": "1.33.0",
+ "lightningcss-linux-arm64-gnu": "1.33.0",
+ "lightningcss-linux-arm64-musl": "1.33.0",
+ "lightningcss-linux-x64-gnu": "1.33.0",
+ "lightningcss-linux-x64-musl": "1.33.0",
+ "lightningcss-win32-arm64-msvc": "1.33.0",
+ "lightningcss-win32-x64-msvc": "1.33.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
+ "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
+ "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
+ "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
+ "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
+ "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
+ "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
+ "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
+ "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
+ "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
+ "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
+ "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.51",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
+ "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.20",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.20.tgz",
+ "integrity": "sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.16",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.9.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz",
+ "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==",
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.7"
+ }
+ },
+ "node_modules/react-toastify": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.1.0.tgz",
+ "integrity": "sha512-e9h23x3phN0wbFeB6yovmWp7lobzV4CaCH0LO8nVP6H7Y+3GbcLpIzMm9dJhcp1RXbpyfvjgpfXqO80QAmn7sg==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19",
+ "react-dom": "^18 || ^19"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.0.tgz",
+ "integrity": "sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@oxc-project/types": "=0.140.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.2.0",
+ "@rolldown/binding-darwin-arm64": "1.2.0",
+ "@rolldown/binding-darwin-x64": "1.2.0",
+ "@rolldown/binding-freebsd-x64": "1.2.0",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.2.0",
+ "@rolldown/binding-linux-arm64-gnu": "1.2.0",
+ "@rolldown/binding-linux-arm64-musl": "1.2.0",
+ "@rolldown/binding-linux-ppc64-gnu": "1.2.0",
+ "@rolldown/binding-linux-s390x-gnu": "1.2.0",
+ "@rolldown/binding-linux-x64-gnu": "1.2.0",
+ "@rolldown/binding-linux-x64-musl": "1.2.0",
+ "@rolldown/binding-openharmony-arm64": "1.2.0",
+ "@rolldown/binding-wasm32-wasi": "1.2.0",
+ "@rolldown/binding-win32-arm64-msvc": "1.2.0",
+ "@rolldown/binding-win32-x64-msvc": "1.2.0"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz",
+ "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.64.0",
+ "@typescript-eslint/parser": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.1.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz",
+ "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.5",
+ "postcss": "^8.5.17",
+ "rolldown": "~1.1.5",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.3.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/vite/node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/vite/node_modules/@oxc-project/types": {
+ "version": "0.139.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz",
+ "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
+ "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
+ "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
+ "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
+ "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
+ "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
+ "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
+ "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
+ "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
+ "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
+ "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz",
+ "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
+ "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
+ "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
+ "@napi-rs/wasm-runtime": "^1.1.6"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
+ "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
+ "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vite/node_modules/rolldown": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz",
+ "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.139.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.1.5",
+ "@rolldown/binding-darwin-arm64": "1.1.5",
+ "@rolldown/binding-darwin-x64": "1.1.5",
+ "@rolldown/binding-freebsd-x64": "1.1.5",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.1.5",
+ "@rolldown/binding-linux-arm64-gnu": "1.1.5",
+ "@rolldown/binding-linux-arm64-musl": "1.1.5",
+ "@rolldown/binding-linux-ppc64-gnu": "1.1.5",
+ "@rolldown/binding-linux-s390x-gnu": "1.1.5",
+ "@rolldown/binding-linux-x64-gnu": "1.1.5",
+ "@rolldown/binding-linux-x64-musl": "1.1.5",
+ "@rolldown/binding-openharmony-arm64": "1.1.5",
+ "@rolldown/binding-wasm32-wasi": "1.1.5",
+ "@rolldown/binding-win32-arm64-msvc": "1.1.5",
+ "@rolldown/binding-win32-x64-msvc": "1.1.5"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/src/mkcd2app/templates/vite-project/package.json b/src/mkcd2app/templates/vite-project/package.json
new file mode 100644
index 0000000..8cab403
--- /dev/null
+++ b/src/mkcd2app/templates/vite-project/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "vite-project",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "lint": "eslint ",
+ "writeLint": "eslint --fix ",
+ "format": "prettier --check ",
+ "writeFormat": "prettier --write ",
+ "preview": "vite preview",
+ "build": "tsc -b && vite build"
+ },
+ "dependencies": {
+ "prettier": "^3.9.5",
+ "react": "^19.2.7",
+ "react-dom": "^19.2.7",
+ "react-toastify": "^11.1.0"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.29.7",
+ "@eslint/js": "^10.0.1",
+ "@rolldown/plugin-babel": "^0.2.3",
+ "@types/babel__core": "^7.20.5",
+ "@types/node": "^24.13.2",
+ "@types/react": "^19.2.17",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.3",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "eslint": "^10.6.0",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.3",
+ "globals": "^17.7.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.62.0",
+ "vite": "^8.1.1"
+ }
+}
diff --git a/src/templates/tauri_files/src-tauri/icons/.keep b/src/mkcd2app/templates/vite-project/public/.keep
similarity index 100%
rename from src/templates/tauri_files/src-tauri/icons/.keep
rename to src/mkcd2app/templates/vite-project/public/.keep
diff --git a/src/templates/website_files/src/App.css b/src/mkcd2app/templates/vite-project/src/App.css
similarity index 93%
rename from src/templates/website_files/src/App.css
rename to src/mkcd2app/templates/vite-project/src/App.css
index 6d6748a..6783cd2 100644
--- a/src/templates/website_files/src/App.css
+++ b/src/mkcd2app/templates/vite-project/src/App.css
@@ -1,7 +1,7 @@
body {
background: black;
overflow: hidden;
- margin: 0
+ margin: 0;
}
iframe {
diff --git a/src/templates/website_files/src/App.tsx b/src/mkcd2app/templates/vite-project/src/App.tsx
similarity index 82%
rename from src/templates/website_files/src/App.tsx
rename to src/mkcd2app/templates/vite-project/src/App.tsx
index 486b744..5a77c84 100644
--- a/src/templates/website_files/src/App.tsx
+++ b/src/mkcd2app/templates/vite-project/src/App.tsx
@@ -5,9 +5,9 @@ import {
loadingToast,
type LoadingToastCallbacks,
} from "./utils/toasts.ts";
-import {toast} from "react-toastify";
-import {GameConfiguration} from "./gameConfiguration.ts";
-import {positionFixedElement} from "./utils/position.ts";
+import { toast } from "react-toastify";
+import { GameConfiguration } from "./gameConfiguration.ts";
+import { positionFixedElement } from "./utils/position.ts";
function App(): React.ReactNode {
const simulatorRef = React.useRef(null);
@@ -21,8 +21,7 @@ function App(): React.ReactNode {
const restartGameToastCallbacksRef = React.useRef(
createEmptyLoadingToastCallbacks(),
);
- const gameCrashToastCloseCallbackRef = React.useRef<() => void>(() => {
- });
+ const gameCrashToastCloseCallbackRef = React.useRef<() => void>(() => {});
const [showNoFocusMessage, setShowNoFocusMessage] = React.useState(false);
const [statsInnerText, setStatsInnerText] = React.useState("");
@@ -31,7 +30,6 @@ function App(): React.ReactNode {
setSimState(JSON.parse(localStorage.getItem("simState") ?? "{}"));
} catch (err) {
console.warn(
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`Failed to load sim state, maybe first time run or simState is empty?\n${err}`,
);
setSimState({});
@@ -46,10 +44,10 @@ function App(): React.ReactNode {
loadingGameToastCallbacksRef.current = GameConfiguration.Toasts
.ENABLE_LOADING_GAME_TOAST
? loadingToast(
- GameConfiguration.Toasts.LOADING_GAME_TOAST_PENDING_MSG,
- GameConfiguration.Toasts.LOADING_GAME_TOAST_SUCCESS_MSG,
- GameConfiguration.Toasts.LOADING_GAME_TOAST_ERROR_MSG,
- )
+ GameConfiguration.Toasts.LOADING_GAME_TOAST_PENDING_MSG,
+ GameConfiguration.Toasts.LOADING_GAME_TOAST_SUCCESS_MSG,
+ GameConfiguration.Toasts.LOADING_GAME_TOAST_ERROR_MSG,
+ )
: createEmptyLoadingToastCallbacks();
fetch("binary.js")
.then((res) => {
@@ -103,7 +101,7 @@ function App(): React.ReactNode {
function stopSim() {
console.log("Stopping simulator");
- simulatorRef.current?.contentWindow?.postMessage({type: "stop"});
+ simulatorRef.current?.contentWindow?.postMessage({ type: "stop" });
}
/* eslint-disable */
@@ -124,10 +122,10 @@ function App(): React.ReactNode {
restartGameToastCallbacksRef.current = GameConfiguration.Toasts
.ENABLE_RESTARTING_GAME_TOAST
? loadingToast(
- GameConfiguration.Toasts.RESTARTING_GAME_TOAST_PENDING_MSG,
- GameConfiguration.Toasts.RESTARTING_GAME_TOAST_SUCCESS_MSG,
- GameConfiguration.Toasts.RESTARTING_GAME_TOAST_ERROR_MSG,
- )
+ GameConfiguration.Toasts.RESTARTING_GAME_TOAST_PENDING_MSG,
+ GameConfiguration.Toasts.RESTARTING_GAME_TOAST_SUCCESS_MSG,
+ GameConfiguration.Toasts.RESTARTING_GAME_TOAST_ERROR_MSG,
+ )
: createEmptyLoadingToastCallbacks();
stopSim();
gameCrashToastCloseCallbackRef.current();
@@ -162,7 +160,7 @@ function App(): React.ReactNode {
console.error(data);
if (GameConfiguration.Toasts.ENABLE_POSSIBLE_GAME_CRASH_TOAST) {
toast.error(
- ({closeToast}) => {
+ ({ closeToast }) => {
gameCrashToastCloseCallbackRef.current = closeToast;
return (
@@ -178,13 +176,13 @@ function App(): React.ReactNode {
restartGameToastCallbacksRef.current = GameConfiguration
.Toasts.ENABLE_RESTARTING_GAME_TOAST
? loadingToast(
- GameConfiguration.Toasts
- .RESTARTING_GAME_TOAST_PENDING_MSG,
- GameConfiguration.Toasts
- .RESTARTING_GAME_TOAST_SUCCESS_MSG,
- GameConfiguration.Toasts
- .RESTARTING_GAME_TOAST_ERROR_MSG,
- )
+ GameConfiguration.Toasts
+ .RESTARTING_GAME_TOAST_PENDING_MSG,
+ GameConfiguration.Toasts
+ .RESTARTING_GAME_TOAST_SUCCESS_MSG,
+ GameConfiguration.Toasts
+ .RESTARTING_GAME_TOAST_ERROR_MSG,
+ )
: createEmptyLoadingToastCallbacks();
stopSim();
setTimeout(() => {
@@ -204,10 +202,10 @@ function App(): React.ReactNode {
},
{
autoClose:
- GameConfiguration.Toasts.POSSIBLE_GAME_CRASH_TOAST_AUTOCLOSE,
+ GameConfiguration.Toasts.POSSIBLE_GAME_CRASH_TOAST_AUTOCLOSE,
closeOnClick:
- GameConfiguration.Toasts
- .POSSIBLE_GAME_CRASH_TOAST_CLOSE_ON_CLICK,
+ GameConfiguration.Toasts
+ .POSSIBLE_GAME_CRASH_TOAST_CLOSE_ON_CLICK,
},
);
}
@@ -233,7 +231,7 @@ function App(): React.ReactNode {
statsRef.current,
GameConfiguration.DebugStats.STATS_LOCATION,
);
- setStatsInnerText(statsText ?? "")
+ setStatsInnerText(statsText ?? "");
statsRef.current.innerText = statsText ?? "";
}
}, 100);
@@ -251,7 +249,7 @@ function App(): React.ReactNode {
const checkFocusID = setInterval(() => {
setShowNoFocusMessage(
!document.hasFocus() ||
- !simulatorRef.current?.contentDocument?.hasFocus(),
+ !simulatorRef.current?.contentDocument?.hasFocus(),
);
}, 100);
@@ -265,7 +263,6 @@ function App(): React.ReactNode {
{statsInnerText}
diff --git a/src/convert/website_to_electron/__init__.py b/src/mkcd2app/templates/vite-project/src/assets/.keep
similarity index 100%
rename from src/convert/website_to_electron/__init__.py
rename to src/mkcd2app/templates/vite-project/src/assets/.keep
diff --git a/src/templates/website_files/src/gameConfiguration.ts b/src/mkcd2app/templates/vite-project/src/gameConfiguration.ts
similarity index 100%
rename from src/templates/website_files/src/gameConfiguration.ts
rename to src/mkcd2app/templates/vite-project/src/gameConfiguration.ts
diff --git a/src/templates/website_files/src/vite-env.d.ts b/src/mkcd2app/templates/vite-project/src/global.d.ts
similarity index 60%
rename from src/templates/website_files/src/vite-env.d.ts
rename to src/mkcd2app/templates/vite-project/src/global.d.ts
index 11f02fe..39fc679 100644
--- a/src/templates/website_files/src/vite-env.d.ts
+++ b/src/mkcd2app/templates/vite-project/src/global.d.ts
@@ -1 +1,3 @@
///
+
+declare module "*.css";
diff --git a/src/templates/website_files/src/main.tsx b/src/mkcd2app/templates/vite-project/src/main.tsx
similarity index 100%
rename from src/templates/website_files/src/main.tsx
rename to src/mkcd2app/templates/vite-project/src/main.tsx
diff --git a/src/templates/website_files/src/utils/position.ts b/src/mkcd2app/templates/vite-project/src/utils/position.ts
similarity index 100%
rename from src/templates/website_files/src/utils/position.ts
rename to src/mkcd2app/templates/vite-project/src/utils/position.ts
diff --git a/src/templates/website_files/src/utils/toasts.ts b/src/mkcd2app/templates/vite-project/src/utils/toasts.ts
similarity index 69%
rename from src/templates/website_files/src/utils/toasts.ts
rename to src/mkcd2app/templates/vite-project/src/utils/toasts.ts
index a41eaf3..8e5ed98 100644
--- a/src/templates/website_files/src/utils/toasts.ts
+++ b/src/mkcd2app/templates/vite-project/src/utils/toasts.ts
@@ -1,20 +1,17 @@
import { toast } from "react-toastify";
export interface LoadingToastCallbacks {
- success: () => void
- error: () => void
-};
+ success: () => void;
+ error: () => void;
+}
-/* eslint-disable @typescript-eslint/no-empty-function */
export function createEmptyLoadingToastCallbacks(): LoadingToastCallbacks {
return {
success: () => {},
error: () => {},
};
}
-/* eslint-enable @typescript-eslint/no-empty-function */
-/* eslint-disable @typescript-eslint/no-floating-promises */
export function loadingToast(
pending: string,
success: string,
@@ -34,4 +31,3 @@ export function loadingToast(
});
return callbacks;
}
-/* eslint-enable @typescript-eslint/no-floating-promises */
diff --git a/src/templates/website_files/tsconfig.app.json b/src/mkcd2app/templates/vite-project/tsconfig.app.json
similarity index 58%
rename from src/templates/website_files/tsconfig.app.json
rename to src/mkcd2app/templates/vite-project/tsconfig.app.json
index 3625418..1a3a739 100644
--- a/src/templates/website_files/tsconfig.app.json
+++ b/src/mkcd2app/templates/vite-project/tsconfig.app.json
@@ -1,12 +1,17 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
- "module": "ESNext",
+ "target": "es2023",
+ "lib": [
+ "ES2023",
+ "DOM"
+ ],
+ "module": "esnext",
+ "types": [
+ "vite/client"
+ ],
+ "allowArbitraryExtensions": true,
"skipLibCheck": true,
-
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
@@ -14,14 +19,13 @@
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
-
/* Linting */
- "strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
-// "erasableSyntaxOnly": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
},
- "include": ["src"]
+ "include": [
+ "src"
+ ]
}
diff --git a/src/mkcd2app/templates/vite-project/tsconfig.json b/src/mkcd2app/templates/vite-project/tsconfig.json
new file mode 100644
index 0000000..ea9d0cd
--- /dev/null
+++ b/src/mkcd2app/templates/vite-project/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.app.json"
+ },
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ]
+}
diff --git a/src/templates/website_files/tsconfig.node.json b/src/mkcd2app/templates/vite-project/tsconfig.node.json
similarity index 56%
rename from src/templates/website_files/tsconfig.node.json
rename to src/mkcd2app/templates/vite-project/tsconfig.node.json
index d920a0f..3bf20da 100644
--- a/src/templates/website_files/tsconfig.node.json
+++ b/src/mkcd2app/templates/vite-project/tsconfig.node.json
@@ -1,25 +1,27 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
- "target": "ES2022",
- "lib": ["ES2023"],
- "module": "ESNext",
+ "target": "es2023",
+ "lib": [
+ "ES2023"
+ ],
+ "types": [
+ "node"
+ ],
"skipLibCheck": true,
-
/* Bundler mode */
- "moduleResolution": "bundler",
+ "module": "nodenext",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
-
/* Linting */
- "strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
-// "erasableSyntaxOnly": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
},
- "include": ["vite.config.ts"]
+ "include": [
+ "vite.config.ts"
+ ]
}
diff --git a/src/mkcd2app/templates/vite-project/vite.config.ts b/src/mkcd2app/templates/vite-project/vite.config.ts
new file mode 100644
index 0000000..f9c3c7b
--- /dev/null
+++ b/src/mkcd2app/templates/vite-project/vite.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from "vite";
+import react, { reactCompilerPreset } from "@vitejs/plugin-react";
+import babel from "@rolldown/plugin-babel";
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react(), babel({ presets: [reactCompilerPreset()] })],
+});
diff --git a/src/convert/website_to_tauri/__init__.py b/src/mkcd2app/utils/__init__.py
similarity index 100%
rename from src/convert/website_to_tauri/__init__.py
rename to src/mkcd2app/utils/__init__.py
diff --git a/src/utils/logger.py b/src/mkcd2app/utils/logger.py
similarity index 96%
rename from src/utils/logger.py
rename to src/mkcd2app/utils/logger.py
index d2c2d6e..d40fe0b 100644
--- a/src/utils/logger.py
+++ b/src/mkcd2app/utils/logger.py
@@ -56,5 +56,5 @@ def set_all_stdout_logger_levels(level: int):
for nm, lgr in logging.Logger.manager.loggerDict.items():
if not isinstance(lgr, logging.PlaceHolder):
for h in lgr.handlers:
- if "
" in repr(h):
+ if isinstance(h, logging.StreamHandler) and h.stream == sys.stdout:
h.setLevel(level)
diff --git a/src/mkcd2app/utils/resources.py b/src/mkcd2app/utils/resources.py
new file mode 100644
index 0000000..5b740f9
--- /dev/null
+++ b/src/mkcd2app/utils/resources.py
@@ -0,0 +1,18 @@
+from contextlib import contextmanager
+from importlib.resources import as_file, files
+from pathlib import Path
+from typing import Iterator
+
+
+@contextmanager
+def get_template_path(name: str) -> Iterator[Path]:
+ ref = files("mkcd2app").joinpath("templates", name)
+ with as_file(ref) as path:
+ yield path
+
+
+@contextmanager
+def get_js_tools_path() -> Iterator[Path]:
+ ref = files("mkcd2app").joinpath("js_tools")
+ with as_file(ref) as path:
+ yield path
diff --git a/src/mkcd2app/utils/run.py b/src/mkcd2app/utils/run.py
new file mode 100644
index 0000000..f358b66
--- /dev/null
+++ b/src/mkcd2app/utils/run.py
@@ -0,0 +1,29 @@
+import logging
+import subprocess
+from pathlib import Path
+
+from mkcd2app.utils.logger import create_logger
+
+logger = create_logger(name=__name__, level=logging.INFO)
+
+
+class BuildError(Exception):
+ def __init__(self, command, cwd, returncode, output):
+ self.command, self.cwd, self.returncode, self.output = command, cwd, returncode, output
+ super().__init__(f"{command} failed ({returncode}) in {cwd}")
+
+
+def run_cmd(command: str, cwd: Path | str) -> str:
+ """
+ Runs a command and captures its output.
+
+ :param command: The command to run - this is executed as a shell command!
+ :param cwd: The current working directory to execute the command in.
+ :return: The stdout
+ :raises BuildError: If the command fails.
+ """
+ logger.debug(f"Running command `{command}` in {cwd}")
+ proc = subprocess.run(command, shell=True, cwd=cwd, capture_output=True, text=True)
+ if proc.returncode != 0:
+ raise BuildError(command, cwd, proc.returncode, proc.stdout + proc.stderr)
+ return proc.stdout
diff --git a/src/templates/electron_files/README.md b/src/templates/electron_files/README.md
deleted file mode 100644
index 5eccfb2..0000000
--- a/src/templates/electron_files/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# {WEBSITE_NAME}
-
-Electron app of a React TS Vite website static export bundle of {SOURCE}.
-
-Generated by
-[MakeCode-Arcade-to-App](https://github.com/UnsignedArduino/MakeCode-Arcade-to-App).
diff --git a/src/templates/electron_files/forge.config.js b/src/templates/electron_files/forge.config.js
deleted file mode 100644
index 57b68f1..0000000
--- a/src/templates/electron_files/forge.config.js
+++ /dev/null
@@ -1,65 +0,0 @@
-const {FusesPlugin} = require('@electron-forge/plugin-fuses');
-const {FuseV1Options, FuseVersion} = require('@electron/fuses');
-
-module.exports = {
- packagerConfig: {
- asar: true,
- extraResource: ["./src/static/binary.js"],
- icon: "./src/assets/icons/icon"
- },
- rebuildConfig: {},
- makers: [
- {
- name: '@electron-forge/maker-squirrel',
- config: {},
- },
- {
- name: '@electron-forge/maker-zip',
- platforms: ['darwin'],
- },
- {
- name: '@electron-forge/maker-deb',
- config: {},
- },
- {
- name: '@electron-forge/maker-rpm',
- config: {},
- },
- ],
- plugins: [
- {
- name: '@electron-forge/plugin-auto-unpack-natives',
- config: {},
- },
- {
- name: '@electron-forge/plugin-webpack',
- config: {
- mainConfig: './webpack.main.config.js',
- renderer: {
- config: './webpack.renderer.config.js',
- entryPoints: [
- {
- html: './src/static/index.html',
- js: './src/renderer.js',
- name: 'main_window',
- preload: {
- js: './src/preload.js',
- },
- },
- ],
- },
- },
- },
- // Fuses are used to enable/disable various Electron functionality
- // at package time, before code signing the application
- new FusesPlugin({
- version: FuseVersion.V1,
- [FuseV1Options.RunAsNode]: false,
- [FuseV1Options.EnableCookieEncryption]: true,
- [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
- [FuseV1Options.EnableNodeCliInspectArguments]: false,
- [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
- [FuseV1Options.OnlyLoadAppFromAsar]: true,
- }),
- ],
-};
diff --git a/src/templates/electron_files/package.json b/src/templates/electron_files/package.json
deleted file mode 100644
index eada747..0000000
--- a/src/templates/electron_files/package.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "name": "NAME",
- "productName": "NAME",
- "author": "AUTHOR",
- "version": "VERSION",
- "description": "DESCRIPTION",
- "main": ".webpack/main",
- "scripts": {
- "start": "electron-forge start",
- "package": "electron-forge package",
- "make": "electron-forge make",
- "publish": "electron-forge publish",
- "lint": "echo \"No linting configured\""
- },
- "keywords": [],
- "devDependencies": {
- "@electron-forge/cli": "^7.8.1",
- "@electron-forge/maker-deb": "^7.8.1",
- "@electron-forge/maker-rpm": "^7.8.1",
- "@electron-forge/maker-squirrel": "^7.8.1",
- "@electron-forge/maker-zip": "^7.8.1",
- "@electron-forge/plugin-auto-unpack-natives": "^7.8.1",
- "@electron-forge/plugin-fuses": "^7.8.1",
- "@electron-forge/plugin-webpack": "^7.8.1",
- "@electron/fuses": "^1.8.0",
- "@vercel/webpack-asset-relocator-loader": "1.7.3",
- "css-loader": "^6.11.0",
- "electron": "36.2.0",
- "copy-webpack-plugin": "^13.0.0",
- "node-loader": "^2.1.0",
- "style-loader": "^3.3.4"
- },
- "dependencies": {
- "electron-squirrel-startup": "^1.0.1"
- }
-}
diff --git a/src/templates/electron_files/src/main.js b/src/templates/electron_files/src/main.js
deleted file mode 100644
index 95f70e8..0000000
--- a/src/templates/electron_files/src/main.js
+++ /dev/null
@@ -1,73 +0,0 @@
-const {app, BrowserWindow, protocol, net} = require("electron")
-const path = require("node:path")
-const fs = require("node:fs")
-const url = require("node:url")
-
-// Handle creating/removing shortcuts on Windows when installing/uninstalling.
-if (require('electron-squirrel-startup')) {
- app.quit();
-}
-
-const windowScale = 4;
-
-const createWindow = () => {
- // Create the browser window.
- const mainWindow = new BrowserWindow({
- width: 160 * windowScale,
- height: 127 * windowScale,
- autoHideMenuBar: true,
- webPreferences: {
- preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
- },
- icon: "./src/assets/icons/icon.png",
- });
-
- // and load the index.html of the app.
- mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
-
- // Open the DevTools.
- // mainWindow.webContents.openDevTools();
-};
-
-// This method will be called when Electron has finished
-// initialization and is ready to create browser windows.
-// Some APIs can only be used after this event occurs.
-app.whenReady().then(() => {
- createWindow();
-
- // On OS X it's common to re-create a window in the app when the
- // dock icon is clicked and there are no other windows open.
- app.on('activate', () => {
- if (BrowserWindow.getAllWindows().length === 0) {
- createWindow();
- }
- });
-
- console.log("Will intercept file requests for binary.js");
- console.log(`process.resourcesPath: ${process.resourcesPath}`);
- protocol.handle('file', (rq) => {
- const u = new URL(rq.url);
- const filePath = u.pathname;
- if (filePath.endsWith("binary.js")) {
- const newPath = path.join(process.resourcesPath, "binary.js");
- const newURL = url.pathToFileURL(newPath);
- console.log(`Requested for ${filePath}, serving ${newURL} instead`);
- return net.fetch(newURL.toString(), {bypassCustomProtocolHandlers: true});
- } else {
- console.log(`Requested for ${filePath}`);
- return net.fetch(rq, {bypassCustomProtocolHandlers: true});
- }
- });
-});
-
-// Quit when all windows are closed, except on macOS. There, it's common
-// for applications and their menu bar to stay active until the user quits
-// explicitly with Cmd + Q.
-app.on('window-all-closed', () => {
- if (process.platform !== 'darwin') {
- app.quit();
- }
-});
-
-// In this file you can include the rest of your app's specific main process
-// code. You can also put them in separate files and import them here.
diff --git a/src/templates/electron_files/src/preload.js b/src/templates/electron_files/src/preload.js
deleted file mode 100644
index 5e9d369..0000000
--- a/src/templates/electron_files/src/preload.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// See the Electron documentation for details on how to use preload scripts:
-// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
diff --git a/src/templates/electron_files/src/renderer.js b/src/templates/electron_files/src/renderer.js
deleted file mode 100644
index d8ef8f1..0000000
--- a/src/templates/electron_files/src/renderer.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * This file will automatically be loaded by webpack and run in the "renderer" context.
- * To learn more about the differences between the "main" and the "renderer" context in
- * Electron, visit:
- *
- * https://electronjs.org/docs/tutorial/process-model
- *
- * By default, Node.js integration in this file is disabled. When enabling Node.js integration
- * in a renderer process, please be aware of potential security implications. You can read
- * more about security risks here:
- *
- * https://electronjs.org/docs/tutorial/security
- *
- * To enable Node.js integration in this file, open up `main.js` and enable the `nodeIntegration`
- * flag:
- *
- * ```
- * // Create the browser window.
- * mainWindow = new BrowserWindow({
- * width: 800,
- * height: 600,
- * webPreferences: {
- * nodeIntegration: true
- * }
- * });
- * ```
- */
-
-console.log('👋 This message is being logged by "renderer.js", included via webpack');
diff --git a/src/templates/electron_files/webpack.main.config.js b/src/templates/electron_files/webpack.main.config.js
deleted file mode 100644
index 3db351b..0000000
--- a/src/templates/electron_files/webpack.main.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-module.exports = {
- /**
- * This is the main entry point for your application, it's the first file
- * that runs in the main process.
- */
- entry: './src/main.js',
- // Put your normal webpack config below here
- module: {
- rules: require('./webpack.rules'),
- }
-};
diff --git a/src/templates/electron_files/webpack.renderer.config.js b/src/templates/electron_files/webpack.renderer.config.js
deleted file mode 100644
index 4d8c816..0000000
--- a/src/templates/electron_files/webpack.renderer.config.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const rules = require('./webpack.rules');
-const CopyWebpackPlugin = require('copy-webpack-plugin');
-
-rules.push(...[
- {test: /\.css$/, use: [{loader: 'style-loader'}, {loader: 'css-loader'}]}
-]);
-
-module.exports = {
- // Put your normal webpack config below here
- module: {
- rules
- },
- plugins: [
- new CopyWebpackPlugin({
- patterns: [
- {
- from: "./src/static",
- to: "./main_window",
- globOptions: {
- ignore: ["**/index.html"]
- }
- }
- ],
-
- })
- ]
-};
diff --git a/src/templates/electron_files/webpack.rules.js b/src/templates/electron_files/webpack.rules.js
deleted file mode 100644
index 23cb22a..0000000
--- a/src/templates/electron_files/webpack.rules.js
+++ /dev/null
@@ -1,35 +0,0 @@
-module.exports = [
- // Add support for native node modules
- {
- // We're specifying native_modules in the test because the asset relocator loader generates a
- // "fake" .node file which is really a cjs file.
- test: /native_modules[/\\].+\.node$/,
- use: 'node-loader',
- },
- {
- test: /[/\\]node_modules[/\\].+\.(m?js|node)$/,
- parser: { amd: false },
- use: {
- loader: '@vercel/webpack-asset-relocator-loader',
- options: {
- outputAssetBase: 'native_modules',
- },
- },
- },
- // Put your webpack loader rules in this array. This is where you would put
- // your ts-loader configuration for instance:
- /**
- * Typescript Example:
- *
- * {
- * test: /\.tsx?$/,
- * exclude: /(node_modules|.webpack)/,
- * loaders: [{
- * loader: 'ts-loader',
- * options: {
- * transpileOnly: true
- * }
- * }]
- * }
- */
-];
diff --git a/src/templates/tauri_files/README.md b/src/templates/tauri_files/README.md
deleted file mode 100644
index 92d2f29..0000000
--- a/src/templates/tauri_files/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# {WEBSITE_NAME}
-
-Tauri app of a React TS Vite website static export bundle of {SOURCE}.
-
-Generated by
-[MakeCode-Arcade-to-App](https://github.com/UnsignedArduino/MakeCode-Arcade-to-App).
diff --git a/src/templates/tauri_files/package.json b/src/templates/tauri_files/package.json
deleted file mode 100644
index 3d2d099..0000000
--- a/src/templates/tauri_files/package.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "tauri-app",
- "private": true,
- "version": "0.1.0",
- "type": "module",
- "scripts": {
- "tauri": "tauri"
- },
- "devDependencies": {
- "@tauri-apps/cli": "^2"
- }
-}
diff --git a/src/templates/tauri_files/src-tauri/.gitignore b/src/templates/tauri_files/src-tauri/.gitignore
deleted file mode 100644
index b21bd68..0000000
--- a/src/templates/tauri_files/src-tauri/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-# Generated by Cargo
-# will have compiled files and executables
-/target/
-
-# Generated by Tauri
-# will have schema files for capabilities auto-completion
-/gen/schemas
diff --git a/src/templates/tauri_files/src-tauri/Cargo.toml b/src/templates/tauri_files/src-tauri/Cargo.toml
deleted file mode 100644
index 6875383..0000000
--- a/src/templates/tauri_files/src-tauri/Cargo.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[package]
-name = "tauri-app"
-version = "0.1.0"
-description = "A Tauri App"
-authors = ["you"]
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[lib]
-# The `_lib` suffix may seem redundant but it is necessary
-# to make the lib name unique and wouldn't conflict with the bin name.
-# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
-name = "tauri_app_lib"
-crate-type = ["staticlib", "cdylib", "rlib"]
-
-[build-dependencies]
-tauri-build = { version = "2", features = [] }
-
-[dependencies]
-tauri = { version = "2", features = [] }
-tauri-plugin-opener = "2"
-serde = { version = "1", features = ["derive"] }
-serde_json = "1"
-
diff --git a/src/templates/tauri_files/src-tauri/build.rs b/src/templates/tauri_files/src-tauri/build.rs
deleted file mode 100644
index d860e1e..0000000
--- a/src/templates/tauri_files/src-tauri/build.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-fn main() {
- tauri_build::build()
-}
diff --git a/src/templates/tauri_files/src-tauri/capabilities/default.json b/src/templates/tauri_files/src-tauri/capabilities/default.json
deleted file mode 100644
index 4cdbf49..0000000
--- a/src/templates/tauri_files/src-tauri/capabilities/default.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "$schema": "../gen/schemas/desktop-schema.json",
- "identifier": "default",
- "description": "Capability for the main window",
- "windows": ["main"],
- "permissions": [
- "core:default",
- "opener:default"
- ]
-}
diff --git a/src/templates/tauri_files/src-tauri/src/lib.rs b/src/templates/tauri_files/src-tauri/src/lib.rs
deleted file mode 100644
index 4a277ef..0000000
--- a/src/templates/tauri_files/src-tauri/src/lib.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
-#[tauri::command]
-fn greet(name: &str) -> String {
- format!("Hello, {}! You've been greeted from Rust!", name)
-}
-
-#[cfg_attr(mobile, tauri::mobile_entry_point)]
-pub fn run() {
- tauri::Builder::default()
- .plugin(tauri_plugin_opener::init())
- .invoke_handler(tauri::generate_handler![greet])
- .run(tauri::generate_context!())
- .expect("error while running tauri application");
-}
diff --git a/src/templates/tauri_files/src-tauri/src/main.rs b/src/templates/tauri_files/src-tauri/src/main.rs
deleted file mode 100644
index 2abccd9..0000000
--- a/src/templates/tauri_files/src-tauri/src/main.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-// Prevents additional console window on Windows in release, DO NOT REMOVE!!
-#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-
-fn main() {
- tauri_app_lib::run()
-}
diff --git a/src/templates/tauri_files/src-tauri/tauri.conf.json b/src/templates/tauri_files/src-tauri/tauri.conf.json
deleted file mode 100644
index 3d78041..0000000
--- a/src/templates/tauri_files/src-tauri/tauri.conf.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "$schema": "https://schema.tauri.app/config/2",
- "productName": "tauri-app",
- "version": "0.1.0",
- "identifier": "com.tauri-app.app",
- "build": {
- "frontendDist": "../src"
- },
- "app": {
- "withGlobalTauri": true,
- "windows": [
- {
- "title": "tauri-app",
- "width": 800,
- "height": 600
- }
- ],
- "security": {
- "csp": null
- }
- },
- "bundle": {
- "active": true,
- "targets": "all",
- "icon": [
- "icons/icon.png",
- "icons/icon.icns",
- "icons/icon.ico"
- ]
- }
-}
diff --git a/src/templates/website_files/README.md b/src/templates/website_files/README.md
deleted file mode 100644
index 4875af7..0000000
--- a/src/templates/website_files/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# {WEBSITE_NAME}
-
-Bundle of {SOURCE} to a React TS Vite website.
-
-Generated by
-[MakeCode-Arcade-to-App](https://github.com/UnsignedArduino/MakeCode-Arcade-to-App).
diff --git a/src/templates/website_files/eslint.config.js b/src/templates/website_files/eslint.config.js
deleted file mode 100644
index 920e044..0000000
--- a/src/templates/website_files/eslint.config.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import js from "@eslint/js";
-import globals from "globals";
-import reactHooks from "eslint-plugin-react-hooks";
-import reactRefresh from "eslint-plugin-react-refresh";
-import reactX from "eslint-plugin-react-x";
-import reactDom from "eslint-plugin-react-dom";
-import tseslint from "typescript-eslint";
-
-export default tseslint.config(
- { ignores: ["dist", "public"] },
- {
- extends: [
- js.configs.recommended,
- ...tseslint.configs.recommendedTypeChecked,
- // ...tseslint.configs.strictTypeChecked,
- ...tseslint.configs.stylisticTypeChecked,
- ],
- files: ["**/*.{ts,tsx}"],
- languageOptions: {
- ecmaVersion: 2020,
- globals: globals.browser,
- parserOptions: {
- project: ["./tsconfig.node.json", "./tsconfig.app.json"],
- tsconfigRootDir: import.meta.dirname,
- },
- },
- plugins: {
- "react-hooks": reactHooks,
- "react-refresh": reactRefresh,
- "react-x": reactX,
- "react-dom": reactDom,
- },
- rules: {
- ...reactHooks.configs.recommended.rules,
- "react-refresh/only-export-components": [
- "warn",
- { allowConstantExport: true },
- ],
- ...reactX.configs["recommended-typescript"].rules,
- ...reactDom.configs.recommended.rules,
- },
- },
-);
diff --git a/src/templates/website_files/package.json b/src/templates/website_files/package.json
deleted file mode 100644
index 1bd8280..0000000
--- a/src/templates/website_files/package.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "name": "NAME",
- "description": "DESCRIPTION",
- "author": "AUTHOR",
- "private": true,
- "version": "VERSION",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "lint": "eslint .",
- "writeLint": "eslint --fix .",
- "format": "prettier --check .",
- "writeFormat": "prettier --write .",
- "build": "tsc -b && vite build",
- "preview": "vite preview"
- },
- "dependencies": {
- "react": "^19.0.0",
- "react-dom": "^19.0.0",
- "react-toastify": "^11.0.5"
- },
- "devDependencies": {
- "@eslint/js": "^9.21.0",
- "@types/react": "^19.0.10",
- "@types/react-dom": "^19.0.4",
- "@vitejs/plugin-react": "^4.3.4",
- "eslint": "^9.21.0",
- "eslint-plugin-react-dom": "^1.42.1",
- "eslint-plugin-react-hooks": "^5.1.0",
- "eslint-plugin-react-refresh": "^0.4.19",
- "eslint-plugin-react-x": "^1.42.1",
- "globals": "^15.15.0",
- "prettier": "^3.5.3",
- "typescript": "~5.8.3",
- "typescript-eslint": "^8.24.1",
- "vite": "^6.2.0"
- },
- "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
-}
diff --git a/src/templates/website_files/src/global.d.ts b/src/templates/website_files/src/global.d.ts
deleted file mode 100644
index 35306c6..0000000
--- a/src/templates/website_files/src/global.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-declare module '*.css';
diff --git a/src/templates/website_files/tsconfig.json b/src/templates/website_files/tsconfig.json
deleted file mode 100644
index 1ffef60..0000000
--- a/src/templates/website_files/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "files": [],
- "references": [
- { "path": "./tsconfig.app.json" },
- { "path": "./tsconfig.node.json" }
- ]
-}
diff --git a/src/templates/website_files/vite.config.ts b/src/templates/website_files/vite.config.ts
deleted file mode 100644
index a14bb4d..0000000
--- a/src/templates/website_files/vite.config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
-
-// https://vite.dev/config/
-export default defineConfig({
- plugins: [react()],
- base: "./"
-})
diff --git a/src/utils/__init__.py b/src/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/src/utils/cmd.py b/src/utils/cmd.py
deleted file mode 100644
index dc20a28..0000000
--- a/src/utils/cmd.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import logging
-import subprocess
-from os import PathLike
-from pathlib import Path
-from typing import Optional, Sequence
-
-from .logger import create_logger
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-
-def run_command(command: str | bytes | PathLike[str] | PathLike[bytes] | Sequence[
- str | bytes | PathLike[str] | PathLike[bytes]], cwd: Optional[Path] = None):
- """
- Run a command in the specified directory.
-
- :param command: The command to run.
- :param cwd: The directory in which to run the command.
- """
- if cwd:
- logger.debug(f"Running command in {cwd}: {command}")
- else:
- logger.debug(f"Running command: {command}")
- subprocess.run(command, cwd=cwd, check=True)
-
-
-def run_shell_command(command: str, cwd: Optional[Path] = None):
- """
- Run a shell command in the specified directory.
-
- :param command: The shell command to run.
- :param cwd: The directory in which to run the command.
- """
- if cwd:
- logger.debug(f"Running command in {cwd}: {command}")
- else:
- logger.debug(f"Running command: {command}")
- subprocess.run(command, cwd=cwd, shell=True, check=True)
diff --git a/src/utils/filesystem.py b/src/utils/filesystem.py
deleted file mode 100644
index c88a1e0..0000000
--- a/src/utils/filesystem.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import logging
-import shutil
-from pathlib import Path
-
-from .logger import create_logger
-
-logger = create_logger(name=__name__, level=logging.INFO)
-
-
-def copy_these(dirs_and_files: list[str], src_dir: Path, dest_dir: Path):
- """
- Copies the specified directories and files in the src_dir to the dest_dir.
-
- :param dirs_and_files: A list of file names to copy.
- :param src_dir: The source directory from which to copy the files.
- :param dest_dir: The destination directory to which the files will be copied.
- """
- logger.debug(f"Copying {len(dirs_and_files)} directories and files")
- for file in dirs_and_files:
- src_path = src_dir / file
- dest_path = dest_dir / file
- if src_path.exists():
- logger.debug(f"Copying {src_path} to {dest_path}")
- if src_path.is_dir():
- shutil.copytree(src_path, dest_path, dirs_exist_ok=True)
- else:
- shutil.copy2(src_path, dest_path)
- else:
- logger.debug(f"{src_path} does not exist, skipping copy.")
-
-
-def delete_these(dirs_and_files: list[str], dir: Path):
- """
- Deletes the specified directories and files from the given directory.
-
- :param dirs_and_files: A list of file names to delete.
- :param dir: The directory from which to delete the files.
- """
- logger.debug(f"Searching for {len(dirs_and_files)} directories and files to delete")
- for file in dirs_and_files:
- file_path = dir / file
- if file_path.exists():
- logger.debug(f"Deleting {file_path}")
- if file_path.is_dir():
- shutil.rmtree(file_path)
- else:
- file_path.unlink()
- else:
- logger.debug(f"{file_path} does not exist, skipping deletion.")
diff --git a/yarn.lock b/yarn.lock
deleted file mode 100644
index 27cd5b6..0000000
--- a/yarn.lock
+++ /dev/null
@@ -1,154 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@xmldom/xmldom@^0.9.8":
- version "0.9.8"
- resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.9.8.tgz#1471e82bdff9e8f20ee8bbe60d4ffa8a516e78d8"
- integrity sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==
-
-ansi-styles@^4.1.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
- integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
- dependencies:
- color-convert "^2.0.1"
-
-balanced-match@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
- integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-
-brace-expansion@^1.1.7:
- version "1.1.12"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
- integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
- dependencies:
- balanced-match "^1.0.0"
- concat-map "0.0.1"
-
-chalk@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
- integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
-color-convert@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
- integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
- dependencies:
- color-name "~1.1.4"
-
-color-name@~1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
- integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-
-commander@^8.2.0:
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
- integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
-
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
- integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
-
-fs.realpath@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
- integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
-
-glob@^7.2.0:
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
- integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.1.1"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-has-flag@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
- integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-inflight@^1.0.4:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
- integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
- integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-
-makecode-core@^1.7.3:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/makecode-core/-/makecode-core-1.7.3.tgz#43c4a3ea07f43d0800d28b8e9aeba2ff0d735718"
- integrity sha512-zrKtFrQPdoKEKcdMz8bbzLDf1Q6GHM7LWrEijCve6v3hQGsm3hGi+AHmd2IFHF6yCV1RNV2CduDABPFYCWyvuw==
- dependencies:
- "@xmldom/xmldom" "^0.9.8"
- chalk "^4.1.2"
-
-makecode@^1.3.4:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/makecode/-/makecode-1.3.4.tgz#6830fbc4a4bcbd239dbfe5f40e30457eae85d956"
- integrity sha512-hwa5zLeQWyV/VY3YtSS+pDJhopp4tyFvqIyesIWJy6GrXl0y+46niITsuNYa/7hzF6s2ysuOfYxe8n/CDxhgFw==
- dependencies:
- chalk "^4.1.2"
- commander "^8.2.0"
- glob "^7.2.0"
- makecode-core "^1.7.3"
- node-watch "^0.7.2"
- semver "^7.3.7"
-
-minimatch@^3.1.1:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
- integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
- dependencies:
- brace-expansion "^1.1.7"
-
-node-watch@^0.7.2:
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/node-watch/-/node-watch-0.7.4.tgz#34557106948cd4b8ddff9aa3d284774004548824"
- integrity sha512-RinNxoz4W1cep1b928fuFhvAQ5ag/+1UlMDV7rbyGthBIgsiEouS4kvRayvvboxii4m8eolKOIBo3OjDqbc+uQ==
-
-once@^1.3.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
- dependencies:
- wrappy "1"
-
-path-is-absolute@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
- integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
-
-semver@^7.3.7:
- version "7.7.2"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
- integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
-
-supports-color@^7.1.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
- integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
- dependencies:
- has-flag "^4.0.0"
-
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==