diff --git a/.github/workflows/FusionWebUI.yml b/.github/workflows/FusionWebUI.yml new file mode 100644 index 0000000000..283fd3fcb4 --- /dev/null +++ b/.github/workflows/FusionWebUI.yml @@ -0,0 +1,73 @@ +name: Fusion - WebUI Build and Format + +on: + workflow_dispatch: {} + + push: + branches: [ prod, dev ] + paths: + - 'exporter/SynthesisFusionAddin/web/**' + pull_request: + branches: [ prod, dev ] + paths: + - 'exporter/SynthesisFusionAddin/web/**' + + +jobs: + runFormatValidationScript: + defaults: + run: + working-directory: exporter/SynthesisFusionAddin/web + name: Biome Format Validation + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Bun Runtime Setup + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Cache Node Dependencies + uses: actions/cache@v3 + with: + key: "${{runner.os}}-npm-fusion-${{hashFiles('exporter/SynthesisFusionAddin/web/bun.lock')}}" + path: 'exporter/SynthesisFusionAddin/web/node_modules' + restore-keys: | + ${{runner.os}}-npm-fusion- + ${{runner.os}}-npm + + - name: Install Dependencies + run: bun install --frozen-lockfile + + - name: Linter & Formatter + run: | + bunx biome --version + bunx biome ci --error-on-warnings + + runBuildScript: + name: Build + runs-on: ubuntu-latest + defaults: + run: + working-directory: exporter/SynthesisFusionAddin/web + steps: + - name: Checkout Code + uses: actions/checkout@v4 + - name: Bun Runtime Setup + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Cache Node Dependencies + uses: actions/cache@v3 + with: + key: "${{runner.os}}-npm-fusion-${{hashFiles('exporter/SynthesisFusionAddin/web/bun.lock')}}" + path: 'exporter/SynthesisFusionAddin/web/node_modules' + restore-keys: | + ${{runner.os}}-npm-fusion- + ${{runner.os}}-npm + + - name: Install Dependencies + run: bun install --frozen-lockfile + + - name: Build + run: bun run build \ No newline at end of file diff --git a/exporter/SynthesisFusionAddin/.gitignore b/exporter/SynthesisFusionAddin/.gitignore index 9e33b772e7..9d1d52fdc0 100644 --- a/exporter/SynthesisFusionAddin/.gitignore +++ b/exporter/SynthesisFusionAddin/.gitignore @@ -110,3 +110,6 @@ site-packages proto/proto_out .aps_auth + + +src/Resources/**/*.png \ No newline at end of file diff --git a/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py b/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py index e85b577397..5fe783f96f 100644 --- a/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py +++ b/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py @@ -7,12 +7,13 @@ import os import platform from dataclasses import dataclass, field, fields +from typing import Any import adsk.core from adsk.fusion import CalculationAccuracy, TriangleMeshQualityOptions from src import INTERNAL_ID -from src.Logging import logFailure, timed +from src.Logging import getLogger, logFailure, timed from src.Types import ( KG, ExportLocation, @@ -68,7 +69,19 @@ def readFromDesign(self) -> "ExporterOptions": for field in fields(self): attribute = designAttributes.itemByName(INTERNAL_ID, field.name) if attribute: - attrJsonData = makeObjectFromJson(type(field.type), json.loads(attribute.value)) + attrJsonData = makeObjectFromJson(field.type, json.loads(attribute.value)) + setattr(self, field.name, attrJsonData) + + self.visualQuality = TriangleMeshQualityOptions.LowQualityTriangleMesh + return self + + @logFailure + @timed + def readFromJSON(self, data: dict[str, Any]) -> "ExporterOptions": + for field in fields(self): + attribute = data.get(field.name) + if attribute is not None: + attrJsonData = makeObjectFromJson(field.type, attribute) setattr(self, field.name, attrJsonData) self.visualQuality = TriangleMeshQualityOptions.LowQualityTriangleMesh @@ -81,3 +94,12 @@ def writeToDesign(self) -> None: for field in fields(self): data = json.dumps(getattr(self, field.name), default=encodeNestedObjects, indent=4) designAttributes.add(INTERNAL_ID, field.name, data) + + @logFailure + @timed + def writeToJson(self) -> dict[str, Any]: + out = {} + for field in fields(self): + data = json.dumps(getattr(self, field.name), default=encodeNestedObjects, indent=4) + out[field.name] = json.loads(data) + return out diff --git a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py index e2693481e9..15bf650111 100644 --- a/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py +++ b/exporter/SynthesisFusionAddin/src/Parser/SynthesisParser/Parser.py @@ -45,6 +45,7 @@ def __init__(self, options: ExporterOptions): @timed def export(self) -> None: + getLogger().info(f"Exporting with options {self.exporterOptions}") app = adsk.core.Application.get() design: adsk.fusion.Design = app.activeDocument.design diff --git a/exporter/SynthesisFusionAddin/src/Types.py b/exporter/SynthesisFusionAddin/src/Types.py index 173f8a84ef..a68bd1fdb5 100644 --- a/exporter/SynthesisFusionAddin/src/Types.py +++ b/exporter/SynthesisFusionAddin/src/Types.py @@ -104,19 +104,19 @@ def encodeNestedObjects(obj: Any) -> Any: return obj -def makeObjectFromJson(objType: type, data: Any) -> Any: +# This function was previously taking type(field.type) instead of just field.type, but it didn't seem to be able to deal with lists like that, and this version does seem to be working in all the places where it's used +def makeObjectFromJson(objType: type[Any] | str | Any, data: Any) -> Any: if isinstance(objType, EnumType): return objType(data) - elif isinstance(objType, PRIMITIVES) or isinstance(data, PRIMITIVES): + elif isinstance(objType, PRIMITIVES) or isinstance(data, PRIMITIVES) or get_origin(objType) is dict: return data elif get_origin(objType) is list: return [makeObjectFromJson(get_args(objType)[0], item) for item in data] - obj = objType() - assert is_dataclass(obj) and isinstance(data, dict), "Found unsupported type to decode." + assert is_dataclass(obj) and isinstance(data, dict), f"Found unsupported type to decode. {objType} {data}" for field in fields(obj): if field.name in data: - setattr(obj, field.name, makeObjectFromJson(type(field.type), data[field.name])) + setattr(obj, field.name, makeObjectFromJson(field.type, data[field.name])) else: setattr(obj, field.name, field.default_factory if field.default_factory is not MISSING else field.default) diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index cf40fc557a..919a5bb3cf 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -3,6 +3,7 @@ """ import importlib +import json import os import re import webbrowser @@ -10,7 +11,9 @@ import adsk.core import adsk.fusion +from adsk.core import Palette +import src.Parser.ExporterOptions as moduleExporterOptions import src.Parser.SynthesisParser.Parser as Parser import src.UI.GamepieceConfigTab as GamepieceConfigTab import src.UI.GeneralConfigTab as GeneralConfigTab @@ -18,18 +21,26 @@ import src.UI.TaggingConfigTab as TaggingConfigTab from src import APP_WEBSITE_URL, Logging, gm from src.APS.APS import getAuth, getUserInfo -from src.Logging import logFailure +from src.Logging import getLogger, logFailure from src.Parser.ExporterOptions import ExporterOptions +from src.Parser.SynthesisParser.Utilities import guid_occurrence from src.Types import SELECTABLE_JOINT_TYPES, ExportLocation, ExportMode from src.UI import FileDialogConfig from src.UI.Handlers import PersistentEventHandler +from src.Util import convertMassUnitsTo, designMassCalculation generalConfigTab: GeneralConfigTab.GeneralConfigTab jointConfigTab: JointConfigTab.JointConfigTab gamepieceConfigTab: GamepieceConfigTab.GamepieceConfigTab taggingConfigTab: TaggingConfigTab.TaggingConfigTab +exporterPalette: Palette +logger = getLogger() + INPUTS_ROOT: adsk.core.CommandInputs +PALETTE_ID = "synthesis_configure" +USE_NEW_UI = True +USE_OLD_UI = False # allow both independently for testing logger = Logging.getLogger() @@ -41,6 +52,7 @@ def reload() -> None: importlib.reload(JointConfigTab) importlib.reload(TaggingConfigTab) + importlib.reload(moduleExporterOptions) importlib.reload(Parser) Parser.reload() @@ -54,93 +66,308 @@ def __init__(self, configure: Any) -> None: @logFailure(messageBox=True) def notify(self, args: adsk.core.CommandCreatedEventArgs) -> None: cmd = args.command - global INPUTS_ROOT - INPUTS_ROOT = cmd.commandInputs - gm.ui.activeSelections.clear() - onExecute = ConfigureCommandExecuteHandler() - cmd.execute.add(onExecute) + if USE_OLD_UI: + global INPUTS_ROOT + INPUTS_ROOT = cmd.commandInputs - onInputChanged = ConfigureCommandInputChanged() - cmd.inputChanged.add(onInputChanged) + onExecute = ConfigureCommandExecuteHandler() + cmd.execute.add(onExecute) - onExecutePreview = CommandExecutePreviewHandler() - cmd.executePreview.add(onExecutePreview) + onInputChanged = ConfigureCommandInputChanged() + cmd.inputChanged.add(onInputChanged) - onSelect = MySelectHandler() - cmd.select.add(onSelect) + onExecutePreview = CommandExecutePreviewHandler() + cmd.executePreview.add(onExecutePreview) - onPreSelectEnd = MyPreselectEndHandler(cmd) - cmd.preSelectEnd.add(onPreSelectEnd) + onSelect = MySelectHandler() + cmd.select.add(onSelect) - onDestroy = MyCommandDestroyHandler() - cmd.destroy.add(onDestroy) + onPreSelectEnd = MyPreselectEndHandler(cmd) + cmd.preSelectEnd.add(onPreSelectEnd) - exporterOptions = ExporterOptions().readFromDesign() or ExporterOptions() + onDestroy = MyCommandDestroyHandler() + cmd.destroy.add(onDestroy) - cmd.isAutoExecute = False - cmd.isExecutedWhenPreEmpted = False - cmd.okButtonText = "Export" - cmd.setDialogSize(800, 350) - cmd.helpFile = os.path.join(".", "src", "Resources", "HTML", "info.html") + exporterOptions = ( + moduleExporterOptions.ExporterOptions().readFromDesign() or moduleExporterOptions.ExporterOptions() + ) - global generalConfigTab - generalConfigTab = GeneralConfigTab.GeneralConfigTab(args, exporterOptions) + cmd.isAutoExecute = True + cmd.isExecutedWhenPreEmpted = False + cmd.okButtonText = "Export" + cmd.helpFile = os.path.join(".", "src", "Resources", "HTML", "info.html") - global gamepieceConfigTab - gamepieceConfigTab = GamepieceConfigTab.GamepieceConfigTab(args, exporterOptions) - generalConfigTab.gamepieceConfigTab = gamepieceConfigTab + global generalConfigTab + generalConfigTab = GeneralConfigTab.GeneralConfigTab(args, exporterOptions) - global jointConfigTab - jointConfigTab = JointConfigTab.JointConfigTab(args) - generalConfigTab.jointConfigTab = jointConfigTab + global gamepieceConfigTab + gamepieceConfigTab = GamepieceConfigTab.GamepieceConfigTab(args, exporterOptions) + generalConfigTab.gamepieceConfigTab = gamepieceConfigTab - global taggingConfigTab - taggingConfigTab = TaggingConfigTab.TaggingConfigTab(args) - generalConfigTab.taggingConfigTab = taggingConfigTab + global jointConfigTab + jointConfigTab = JointConfigTab.JointConfigTab(args) + generalConfigTab.jointConfigTab = jointConfigTab - if not exporterOptions.exportMode == ExportMode.FIELD: - gamepieceConfigTab.isVisible = False + global taggingConfigTab + taggingConfigTab = TaggingConfigTab.TaggingConfigTab(args) + generalConfigTab.taggingConfigTab = taggingConfigTab - if not exporterOptions.exportMode == ExportMode.ROBOT: - jointConfigTab.isVisible = False + if not exporterOptions.exportMode == ExportMode.FIELD: + gamepieceConfigTab.isVisible = False - design = adsk.fusion.Design.cast(adsk.core.Application.get().activeProduct) - for synGamepiece in exporterOptions.gamepieces: - fusionOccurrence = design.findEntityByToken(synGamepiece.occurrenceToken)[0] - gamepieceConfigTab.addGamepiece(fusionOccurrence, synGamepiece) + if not exporterOptions.exportMode == ExportMode.ROBOT: + jointConfigTab.isVisible = False + + design = adsk.fusion.Design.cast(adsk.core.Application.get().activeProduct) + for synGamepiece in exporterOptions.gamepieces: + fusionOccurrence = design.findEntityByToken(synGamepiece.occurrenceToken)[0] + gamepieceConfigTab.addGamepiece(fusionOccurrence, synGamepiece) + + if len(exporterOptions.joints): + for synJoint in exporterOptions.joints: + fusionJoints = design.findEntityByToken(synJoint.jointToken) + if len(fusionJoints): + jointConfigTab.addJoint(fusionJoints[0], synJoint) + else: + for joint in [*design.rootComponent.allJoints, *design.rootComponent.allAsBuiltJoints]: + if joint.jointMotion.jointType in SELECTABLE_JOINT_TYPES and not joint.isSuppressed: + jointConfigTab.addJoint(joint) - if len(exporterOptions.joints): - for synJoint in exporterOptions.joints: - fusionJoints = design.findEntityByToken(synJoint.jointToken) + # Adding saved wheels must take place after joints are added as a result of how the two types are connected. + for wheel in exporterOptions.wheels: + fusionJoints = design.findEntityByToken(wheel.jointToken) if len(fusionJoints): - jointConfigTab.addJoint(fusionJoints[0], synJoint) - else: - for joint in [*design.rootComponent.allJoints, *design.rootComponent.allAsBuiltJoints]: - if joint.jointMotion.jointType in SELECTABLE_JOINT_TYPES and not joint.isSuppressed: - jointConfigTab.addJoint(joint) - - # Adding saved wheels must take place after joints are added as a result of how the two types are connected. - for wheel in exporterOptions.wheels: - fusionJoints = design.findEntityByToken(wheel.jointToken) - if len(fusionJoints): - jointConfigTab.addWheel(fusionJoints[0], wheel) - - if len(exporterOptions.tags): - for token, tag in exporterOptions.tags.items(): - fusionBody = design.findEntityByToken(token) - if len(fusionBody): - taggingConfigTab.addTag(fusionBody[0], tag) - - getAuth() - user_info_result = getUserInfo() - if user_info_result.is_err(): - user_name = "Not Signed In" + jointConfigTab.addWheel(fusionJoints[0], wheel) + + if len(exporterOptions.tags): + for token, tag in exporterOptions.tags.items(): + fusionBody = design.findEntityByToken(token) + if len(fusionBody): + taggingConfigTab.addTag(fusionBody[0], tag) + + getAuth() + user_info_result = getUserInfo() + if user_info_result.is_err(): + user_name = "Not Signed In" + else: + user_name = user_info_result.unwrap().given_name + + apsSettings = INPUTS_ROOT.addTabCommandInput("aps_settings", f"APS Settings ({user_name})") + apsSettings.tooltip = "Configuration settings for Autodesk Platform Services." + + if USE_NEW_UI: + palettes = gm.ui.palettes + global exporterPalette + exporterPalette = palettes.itemById(PALETTE_ID) + if exporterPalette: + exporterPalette.deleteMe() + + exporterPalette = palettes.add( + id=PALETTE_ID, + name="Synthesis Exporter", + htmlFileURL="web/dist/index.html", + isVisible=True, + showCloseButton=True, + isResizable=True, + width=1200, + height=800, + useNewWebBrowser=True, + ) + onMessage = IncomingHTMLMessageHandler() + exporterPalette.incomingFromHTML.add(onMessage) + + onClose = PaletteCloseHandler() + exporterPalette.closed.add(onClose) + + exporterPalette.isVisible = True + + +class PaletteCloseHandler(PersistentEventHandler, adsk.core.UserInterfaceGeneralEventHandler): + @logFailure + def notify(self, e: Any) -> None: + if exporterPalette: + exporterPalette.deleteMe() + + +class IncomingHTMLMessageHandler(PersistentEventHandler, adsk.core.HTMLEventHandler): + @logFailure(messageBox=True) + def notify(self, html_args: adsk.core.HTMLEventArgs) -> None: + data = json.loads(html_args.data) + + if html_args.action == "init": + exporterOptions = ( + moduleExporterOptions.ExporterOptions().readFromDesign() or moduleExporterOptions.ExporterOptions() + ) + + design = adsk.fusion.Design.cast(adsk.core.Application.get().activeProduct) + gamepieceData = [] + for synGamepiece in exporterOptions.gamepieces: # Copy this + fusionOccurrence = design.findEntityByToken(synGamepiece.occurrenceToken)[0] + gamepiece = adsk.fusion.Occurrence.cast(fusionOccurrence) + gamepieceData.append(buildGamepiece(gamepiece)) + + jointData = [] + if len(exporterOptions.joints): + for synJoint in exporterOptions.joints: + fusionJoints = design.findEntityByToken(synJoint.jointToken) + if len(fusionJoints): + try: + joint = adsk.fusion.Joint.cast(fusionJoints[0]) + jointData.append(buildJoint(joint)) + except Exception as e: + logger.error(e) + else: + for joint in [*design.rootComponent.allJoints, *design.rootComponent.allAsBuiltJoints]: + if joint.jointMotion.jointType in SELECTABLE_JOINT_TYPES and not joint.isSuppressed: + try: + jointData.append(buildJoint(joint)) + except Exception as e: + logger.error(e) + + tagData = [] + if len(exporterOptions.tags): + for token, tag in exporterOptions.tags.items(): + fusionBody = design.findEntityByToken(token) + + if len(fusionBody): + body = adsk.fusion.BRepBody.cast(fusionBody[0]) + try: + tagData.append(buildTaggedObject(body)) + except Exception as e: + logger.error(e) + html_args.returnData = json.dumps( + { + "gamepieceData": gamepieceData, + "jointData": jointData, + "tagData": tagData, + "options": exporterOptions.writeToJson(), + "calculatedMass": convertMassUnitsTo(designMassCalculation()), + } + ) + elif html_args.action == "export": + opts = moduleExporterOptions.ExporterOptions().readFromJSON(data) + export(opts) + html_args.returnData = "{}" + elif html_args.action == "save": + opts = moduleExporterOptions.ExporterOptions().readFromJSON(data) + opts.writeToDesign() + html_args.returnData = "{}" + + elif html_args.action == "selectJoint": + try: + selection = gm.app.userInterface.selectEntity("Select Joints", "Joints") + joint = adsk.fusion.Joint.cast(selection.entity) + html_args.returnData = json.dumps(buildJoint(joint)) + except Exception as e: + html_args.returnData = json.dumps({"_err": e.__repr__()}) + logger.error(e) + gm.ui.activeSelections.clear() + elif html_args.action == "selectGamepiece": + try: + selection = gm.app.userInterface.selectEntity("Select Gamepieces", "Occurrences") + rootComponent = adsk.fusion.Design.cast(adsk.core.Application.get().activeProduct).rootComponent + entity = adsk.fusion.Occurrence.cast(selection.entity) + occurrenceList: adsk.fusion.OccurrenceList = rootComponent.allOccurrencesByComponent(entity.component) + gamepieces = [] + for i in range(occurrenceList.count): + occurrence = occurrenceList.item(i) + gamepieces.append(buildGamepiece(occurrence)) + html_args.returnData = json.dumps(gamepieces) + except Exception as e: + html_args.returnData = json.dumps({"_err": e.__repr__()}) + logger.error(e) + gm.ui.activeSelections.clear() + elif html_args.action == "selectBody": + + try: + selection = gm.app.userInterface.selectEntity("Select Body", "SolidBodies,SurfaceBodies") + logger.info(selection) + body = adsk.fusion.BRepBody.cast(selection.entity) + html_args.returnData = json.dumps(buildTaggedObject(body)) + except Exception as e: + html_args.returnData = json.dumps({"_err": e.__repr__()}) + logger.error(e) + + elif html_args.action == "cancelSelection": + gm.ui.terminateActiveCommand() + html_args.returnData = "{}" else: - user_name = user_info_result.unwrap().given_name - - apsSettings = INPUTS_ROOT.addTabCommandInput("aps_settings", f"APS Settings ({user_name})") - apsSettings.tooltip = "Configuration settings for Autodesk Platform Services." + gm.ui.messageBox(f"Event {html_args.action} arrived{json.dumps(data, indent=2)}") + + +def buildJoint(joint: adsk.fusion.Joint) -> dict[str, Any]: + return { + "name": joint.name, + "entityToken": joint.entityToken, + "jointType": joint.jointMotion.jointType, + } + + +def buildTaggedObject(body: adsk.fusion.BRepBody) -> dict[str, Any]: + key_body = body.nativeObject or body + return { + "name": key_body.name, + "componentName": key_body.parentComponent.name, + "entityToken": key_body.entityToken, + } + + +def buildGamepiece(gamepiece: adsk.fusion.Occurrence) -> dict[str, Any]: + physicalProps = gamepiece.component.getPhysicalProperties(adsk.fusion.CalculationAccuracy.LowCalculationAccuracy) + response = { + "name": gamepiece.name, + "occurrenceToken": guid_occurrence(gamepiece), + "mass": physicalProps.mass, + "entityIDs": [gamepiece.entityToken], + } + + def addChildOccurrences(childOccurrences: adsk.fusion.OccurrenceList) -> None: + for i in range(childOccurrences.count): + occ = childOccurrences.item(i) + response["entityIDs"].append(occ.entityToken) + + if occ.childOccurrences: + addChildOccurrences(occ.childOccurrences) + + if gamepiece.childOccurrences: + addChildOccurrences(gamepiece.childOccurrences) + return response + + +@logFailure(messageBox=True) +def export(exporterOptions: moduleExporterOptions.ExporterOptions) -> None: + logger.info("NEWUI") + logger.info(exporterOptions) + design = adsk.fusion.Design.cast(adsk.core.Application.get().activeProduct) + fullName = design.rootComponent.name + versionMatch = re.search(r"v\d+", fullName) + docName = (fullName[: versionMatch.start()].strip() if versionMatch else fullName).replace(" ", "_") + docVersion = versionMatch.group() if versionMatch else "v0" + + processedFileName = gm.app.activeDocument.name.replace(" ", "_") + defaultFileName = f"{'_'.join([docName, docVersion])}.mira" + if exporterOptions.exportLocation == ExportLocation.DOWNLOAD: + savepath = FileDialogConfig.saveFileDialog(exporterOptions.fileLocation, defaultFileName) + else: + savepath = processedFileName + + if not savepath: # User cancelled the save dialog + return + + adsk.doEvents() + exporterOptions.fileLocation = savepath + exporterOptions.name = docName + exporterOptions.version = docVersion + exporterOptions.materials = 0 + + Parser.Parser(exporterOptions).export() + exporterOptions.writeToDesign() + + if exporterOptions.openSynthesisUponExport: + res = webbrowser.open(APP_WEBSITE_URL) + if not res: + gm.ui.messageBox("Failed to open Synthesis in your default browser.") class ConfigureCommandExecuteHandler(PersistentEventHandler, adsk.core.CommandEventHandler): @@ -149,7 +376,9 @@ class ConfigureCommandExecuteHandler(PersistentEventHandler, adsk.core.CommandEv @logFailure(messageBox=True) def notify(self, _: adsk.core.CommandEventArgs) -> None: design = adsk.fusion.Design.cast(adsk.core.Application.get().activeProduct) - exporterOptions = ExporterOptions().readFromDesign() or ExporterOptions() + exporterOptions = ( + moduleExporterOptions.ExporterOptions().readFromDesign() or moduleExporterOptions.ExporterOptions() + ) fullName: str = design.rootComponent.name versionMatch = re.search(r"v\d+", fullName) @@ -176,7 +405,7 @@ def notify(self, _: adsk.core.CommandEventArgs) -> None: selectedGamepieces = gamepieceConfigTab.getGamepieces() selectedTags = taggingConfigTab.getTags() - exporterOptions = ExporterOptions( + exporterOptions = moduleExporterOptions.ExporterOptions( savepath, docName, docVersion, @@ -196,6 +425,8 @@ def notify(self, _: adsk.core.CommandEventArgs) -> None: frictionOverrideCoeff=generalConfigTab.frictionOverrideCoeff, openSynthesisUponExport=generalConfigTab.openSynthesisUponExport, ) + logger.info("OLDUI") + logger.info(exporterOptions) try: Parser.Parser(exporterOptions).export() @@ -269,7 +500,7 @@ class MyCommandDestroyHandler(PersistentEventHandler, adsk.core.CommandEventHand def notify(self, _: adsk.core.CommandEventArgs) -> None: jointConfigTab.reset() gamepieceConfigTab.reset() - + # exporterPalette.deleteMe() design = adsk.fusion.Design.cast(adsk.core.Application.get().activeProduct) for group in design.rootComponent.customGraphicsGroups: group.deleteMe() diff --git a/exporter/SynthesisFusionAddin/src/UI/MarkingMenu.py b/exporter/SynthesisFusionAddin/src/UI/MarkingMenu.py index 30cb8831d7..bccc1bed59 100644 --- a/exporter/SynthesisFusionAddin/src/UI/MarkingMenu.py +++ b/exporter/SynthesisFusionAddin/src/UI/MarkingMenu.py @@ -136,7 +136,6 @@ def notify(self, args: adsk.core.CommandEventArgs) -> None: onMarkingMenuDisplaying = MyMarkingMenuHandler() handlers.append(onMarkingMenuDisplaying) ui.markingMenuDisplaying.add(onMarkingMenuDisplaying) - # Add customized handler for commands creating onCommandCreated = MyCommandCreatedEventHandler() handlers.append(onCommandCreated) diff --git a/exporter/SynthesisFusionAddin/web/.gitignore b/exporter/SynthesisFusionAddin/web/.gitignore new file mode 100644 index 0000000000..963e24b086 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +.vite + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + diff --git a/exporter/SynthesisFusionAddin/web/README.md b/exporter/SynthesisFusionAddin/web/README.md new file mode 100644 index 0000000000..d74bf8b5c5 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/README.md @@ -0,0 +1,6 @@ +# Synthesis Exporter Web UI +This is a React + TS project to build a webpage that will be run in a [Palette](https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-6C0C8148-98D0-4DBC-A4EC-D8E03A8A3B5B) inside of fusion + +For developing in Fusion, use `bun run dev` to automatically recompile your output. You should also enable the web developer tools (see the "Debugging" section of the [palettes help article](https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-6C0C8148-98D0-4DBC-A4EC-D8E03A8A3B5B)) + +If you're only making changes to the frontend and don't require fusion, you can use `bun run dev:ui` to open it in your browser \ No newline at end of file diff --git a/exporter/SynthesisFusionAddin/web/biome.json b/exporter/SynthesisFusionAddin/web/biome.json new file mode 100644 index 0000000000..42eb21f743 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/biome.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.1.1/schema.json", + "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, + "files": { "ignoreUnknown": false }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 4, + "lineEnding": "lf", + "lineWidth": 120, + "attributePosition": "auto", + "bracketSameLine": false, + "bracketSpacing": true, + "expand": "auto", + "useEditorconfig": true, + "includes": ["**", "!dist", "!public", "!node_modules", "!.vite"] + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedImports": "warn" + } + }, + "includes": ["**", "!dist", "!public", "!node_modules", "!.vite"] + }, + "javascript": { + "formatter": { + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "es5", + "semicolons": "asNeeded", + "arrowParentheses": "asNeeded", + "bracketSameLine": false, + "quoteStyle": "double", + "attributePosition": "auto", + "bracketSpacing": true + } + }, + "html": { "formatter": { "selfCloseVoidElements": "always" } }, + "overrides": [ + { "includes": ["**/*.{ts,tsx}"], "javascript": { "globals": [] } }, + { "includes": ["*.json"], "formatter": { "indentWidth": 2 } } + ], + "assist": { + "enabled": true, + "actions": { "source": { "organizeImports": "on" } } + } +} diff --git a/exporter/SynthesisFusionAddin/web/bun.lock b/exporter/SynthesisFusionAddin/web/bun.lock new file mode 100644 index 0000000000..2bc69e14ec --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/bun.lock @@ -0,0 +1,649 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "web", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.2.0", + "@mui/material": "^7.2.0", + "eslint-plugin-react-dom": "^1.52.2", + "eslint-plugin-react-x": "^1.52.2", + "immer": "^10.1.1", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "use-immer": "^0.11.0", + }, + "devDependencies": { + "@biomejs/biome": "2.1.1", + "@eslint/js": "^9.29.0", + "@types/node": "^24.0.14", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react-swc": "^3.10.2", + "eslint": "^9.29.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.2.0", + "prettier": "^3.6.2", + "typescript": "~5.8.3", + "typescript-eslint": "^8.34.1", + "vite": "^6.3.5", + }, + }, + }, + "packages": { + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/generator": ["@babel/generator@7.28.0", "", { "dependencies": { "@babel/parser": "^7.28.0", "@babel/types": "^7.28.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="], + + "@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/types": "^7.28.0", "debug": "^4.3.1" } }, "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg=="], + + "@babel/types": ["@babel/types@7.28.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg=="], + + "@biomejs/biome": ["@biomejs/biome@2.1.1", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.1.1", "@biomejs/cli-darwin-x64": "2.1.1", "@biomejs/cli-linux-arm64": "2.1.1", "@biomejs/cli-linux-arm64-musl": "2.1.1", "@biomejs/cli-linux-x64": "2.1.1", "@biomejs/cli-linux-x64-musl": "2.1.1", "@biomejs/cli-win32-arm64": "2.1.1", "@biomejs/cli-win32-x64": "2.1.1" }, "bin": { "biome": "bin/biome" } }, "sha512-HFGYkxG714KzG+8tvtXCJ1t1qXQMzgWzfvQaUjxN6UeKv+KvMEuliInnbZLJm6DXFXwqVi6446EGI0sGBLIYng=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.1.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2Muinu5ok4tWxq4nu5l19el48cwCY/vzvI7Vjbkf3CYIQkjxZLyj0Ad37Jv2OtlXYaLvv+Sfu1hFeXt/JwRRXQ=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.1.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cC8HM5lrgKQXLAK+6Iz2FrYW5A62pAAX6KAnRlEyLb+Q3+Kr6ur/sSuoIacqlp1yvmjHJqjYfZjPvHWnqxoEIA=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-tw4BEbhAUkWPe4WBr6IX04DJo+2jz5qpPzpW/SWvqMjb9QuHY8+J0M23V8EPY/zWU4IG8Ui0XESapR1CB49Q7g=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/7FBLnTswu4jgV9ttI3AMIdDGqVEPIZd8I5u2D4tfCoj8rl9dnjrEQbAIDlWhUXdyWlFSz8JypH3swU9h9P+2A=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3WJ1GKjU7NzZb6RTbwLB59v9cTIlzjbiFLDB0z4376TkDqoNYilJaC37IomCr/aXwuU8QKkrYoHrgpSq5ffJ4Q=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-kUu+loNI3OCD2c12cUt7M5yaaSjDnGIksZwKnueubX6c/HWUyi/0mPbTBHR49Me3F0KKjWiKM+ZOjsmC+lUt9g=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.1.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-vEHK0v0oW+E6RUWLoxb2isI3rZo57OX9ZNyyGH701fZPj6Il0Rn1f5DMNyCmyflMwTnIQstEbs7n2BxYSqQx4Q=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.1.1", "", { "os": "win32", "cpu": "x64" }, "sha512-i2PKdn70kY++KEF/zkQFvQfX1e8SkA8hq4BgC+yE9dZqyLzB/XStY2MvwI3qswlRgnGpgncgqe0QYKVS1blksg=="], + + "@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="], + + "@emotion/cache": ["@emotion/cache@11.14.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA=="], + + "@emotion/hash": ["@emotion/hash@0.9.2", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="], + + "@emotion/is-prop-valid": ["@emotion/is-prop-valid@1.3.1", "", { "dependencies": { "@emotion/memoize": "^0.9.0" } }, "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw=="], + + "@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="], + + "@emotion/react": ["@emotion/react@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="], + + "@emotion/serialize": ["@emotion/serialize@1.3.3", "", { "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA=="], + + "@emotion/sheet": ["@emotion/sheet@1.4.0", "", {}, "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="], + + "@emotion/styled": ["@emotion/styled@11.14.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/is-prop-valid": "^1.3.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2" }, "peerDependencies": { "@emotion/react": "^11.0.0-rc.0", "react": ">=16.8.0" } }, "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw=="], + + "@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="], + + "@emotion/use-insertion-effect-with-fallbacks": ["@emotion/use-insertion-effect-with-fallbacks@1.2.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg=="], + + "@emotion/utils": ["@emotion/utils@1.4.2", "", {}, "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="], + + "@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.6", "", { "os": "aix", "cpu": "ppc64" }, "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.6", "", { "os": "android", "cpu": "arm" }, "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.6", "", { "os": "android", "cpu": "arm64" }, "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.6", "", { "os": "android", "cpu": "x64" }, "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.6", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.6", "", { "os": "linux", "cpu": "arm" }, "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.6", "", { "os": "linux", "cpu": "ia32" }, "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.6", "", { "os": "linux", "cpu": "ppc64" }, "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.6", "", { "os": "linux", "cpu": "none" }, "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.6", "", { "os": "linux", "cpu": "s390x" }, "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.6", "", { "os": "linux", "cpu": "x64" }, "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.6", "", { "os": "none", "cpu": "arm64" }, "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.6", "", { "os": "none", "cpu": "x64" }, "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.6", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.6", "", { "os": "openbsd", "cpu": "x64" }, "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.6", "", { "os": "none", "cpu": "arm64" }, "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.6", "", { "os": "sunos", "cpu": "x64" }, "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint-react/ast": ["@eslint-react/ast@1.52.2", "", { "dependencies": { "@eslint-react/eff": "1.52.2", "@typescript-eslint/types": "^8.34.0", "@typescript-eslint/typescript-estree": "^8.34.0", "@typescript-eslint/utils": "^8.34.0", "string-ts": "^2.2.1", "ts-pattern": "^5.7.1" } }, "sha512-L0Tbbzx5l7JHgkQ1TqPWQuZ4+PsXDcgtt3056FOYqstUrDRG+5ylm7h3gEWu98I3FDdgLS8q9dOzz0PGgwZCTA=="], + + "@eslint-react/core": ["@eslint-react/core@1.52.2", "", { "dependencies": { "@eslint-react/ast": "1.52.2", "@eslint-react/eff": "1.52.2", "@eslint-react/kit": "1.52.2", "@eslint-react/shared": "1.52.2", "@eslint-react/var": "1.52.2", "@typescript-eslint/scope-manager": "^8.34.0", "@typescript-eslint/type-utils": "^8.34.0", "@typescript-eslint/types": "^8.34.0", "@typescript-eslint/utils": "^8.34.0", "birecord": "^0.1.1", "ts-pattern": "^5.7.1" } }, "sha512-FpxKZJHlf3zXETNL+WQP/SoYuVQNheWm1iDgW68RyHygD8mzk9CnVLDgjMrfmh2n0eaOqnWCL/IC2YzD6VpYOQ=="], + + "@eslint-react/eff": ["@eslint-react/eff@1.52.2", "", {}, "sha512-YBPE2J1+PfXrR9Ct+9rQsw8uRU06zHopI508cfj0usaIBf3hz18V2GoRTVhsjniP0QbvKQdHzyPmmS/B6uyMZQ=="], + + "@eslint-react/kit": ["@eslint-react/kit@1.52.2", "", { "dependencies": { "@eslint-react/eff": "1.52.2", "@typescript-eslint/utils": "^8.34.0", "ts-pattern": "^5.7.1", "zod": "^3.25.63" } }, "sha512-k0cSgFnPlDPI1xyRzHjEWIapLG0zCy7mx1HBLg5wuKf/zzSh3iNFId53xMebR05vM2k9YH63gsvTwRkGx/77Zw=="], + + "@eslint-react/shared": ["@eslint-react/shared@1.52.2", "", { "dependencies": { "@eslint-react/eff": "1.52.2", "@eslint-react/kit": "1.52.2", "@typescript-eslint/utils": "^8.34.0", "ts-pattern": "^5.7.1", "zod": "^3.25.63" } }, "sha512-YHysVcCfmBoxt2+6Ao4HdLPUYNSem70gy+0yzOQvlQFSsGhh+uifQ68SSa/2uJBWfNUm9xQlyDsr2raeO4BlgA=="], + + "@eslint-react/var": ["@eslint-react/var@1.52.2", "", { "dependencies": { "@eslint-react/ast": "1.52.2", "@eslint-react/eff": "1.52.2", "@typescript-eslint/scope-manager": "^8.34.0", "@typescript-eslint/types": "^8.34.0", "@typescript-eslint/utils": "^8.34.0", "string-ts": "^2.2.1", "ts-pattern": "^5.7.1" } }, "sha512-/7IYMPsmO0tIYqkqAVnkqB4eXeVBvgBL/a9hcGCO2eUSzslYzQHSzNPhIoPLD9HXng+0CWlT+KupOFIqP9a26A=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.3.0", "", {}, "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw=="], + + "@eslint/core": ["@eslint/core@0.14.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + + "@eslint/js": ["@eslint/js@9.30.1", "", {}, "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.3", "", { "dependencies": { "@eslint/core": "^0.15.1", "levn": "^0.4.1" } }, "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.4", "", {}, "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], + + "@mui/core-downloads-tracker": ["@mui/core-downloads-tracker@7.2.0", "", {}, "sha512-d49s7kEgI5iX40xb2YPazANvo7Bx0BLg/MNRwv+7BVpZUzXj1DaVCKlQTDex3gy/0jsCb4w7AY2uH4t4AJvSog=="], + + "@mui/icons-material": ["@mui/icons-material@7.2.0", "", { "dependencies": { "@babel/runtime": "^7.27.6" }, "peerDependencies": { "@mui/material": "^7.2.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gRCspp3pfjHQyTmSOmYw7kUQTd9Udpdan4R8EnZvqPeoAtHnPzkvjBrBqzKaoAbbBp5bGF7BcD18zZJh4nwu0A=="], + + "@mui/material": ["@mui/material@7.2.0", "", { "dependencies": { "@babel/runtime": "^7.27.6", "@mui/core-downloads-tracker": "^7.2.0", "@mui/system": "^7.2.0", "@mui/types": "^7.4.4", "@mui/utils": "^7.2.0", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^19.1.0", "react-transition-group": "^4.4.5" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@mui/material-pigment-css": "^7.2.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@mui/material-pigment-css"] }, "sha512-NTuyFNen5Z2QY+I242MDZzXnFIVIR6ERxo7vntFi9K1wCgSwvIl0HcAO2OOydKqqKApE6omRiYhpny1ZhGuH7Q=="], + + "@mui/private-theming": ["@mui/private-theming@7.2.0", "", { "dependencies": { "@babel/runtime": "^7.27.6", "@mui/utils": "^7.2.0", "prop-types": "^15.8.1" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y6N1Yt3T5RMxVFnCh6+zeSWBuQdNDm5/UlM0EAYZzZR/1u+XKJWYQmbpx4e+F+1EpkYi3Nk8KhPiQDi83M3zIw=="], + + "@mui/styled-engine": ["@mui/styled-engine@7.2.0", "", { "dependencies": { "@babel/runtime": "^7.27.6", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yq08xynbrNYcB1nBcW9Fn8/h/iniM3ewRguGJXPIAbHvxEF7Pz95kbEEOAAhwzxMX4okhzvHmk0DFuC5ayvgIQ=="], + + "@mui/system": ["@mui/system@7.2.0", "", { "dependencies": { "@babel/runtime": "^7.27.6", "@mui/private-theming": "^7.2.0", "@mui/styled-engine": "^7.2.0", "@mui/types": "^7.4.4", "@mui/utils": "^7.2.0", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-PG7cm/WluU6RAs+gNND2R9vDwNh+ERWxPkqTaiXQJGIFAyJ+VxhyKfzpdZNk0z0XdmBxxi9KhFOpgxjehf/O0A=="], + + "@mui/types": ["@mui/types@7.4.4", "", { "dependencies": { "@babel/runtime": "^7.27.6" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-p63yhbX52MO/ajXC7hDHJA5yjzJekvWD3q4YDLl1rSg+OXLczMYPvTuSuviPRCgRX8+E42RXz1D/dz9SxPSlWg=="], + + "@mui/utils": ["@mui/utils@7.2.0", "", { "dependencies": { "@babel/runtime": "^7.27.6", "@mui/types": "^7.4.4", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-is": "^19.1.0" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-O0i1GQL6MDzhKdy9iAu5Yr0Sz1wZjROH1o3aoztuivdCXqEeQYnEjTDiRLGuFxI9zrUbTHBwobMyQH5sNtyacw=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.11", "", {}, "sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.2", "", { "os": "android", "cpu": "arm" }, "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.2", "", { "os": "android", "cpu": "arm64" }, "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A=="], + + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g=="], + + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.2", "", { "os": "win32", "cpu": "x64" }, "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA=="], + + "@swc/core": ["@swc/core@1.12.11", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.11", "@swc/core-darwin-x64": "1.12.11", "@swc/core-linux-arm-gnueabihf": "1.12.11", "@swc/core-linux-arm64-gnu": "1.12.11", "@swc/core-linux-arm64-musl": "1.12.11", "@swc/core-linux-x64-gnu": "1.12.11", "@swc/core-linux-x64-musl": "1.12.11", "@swc/core-win32-arm64-msvc": "1.12.11", "@swc/core-win32-ia32-msvc": "1.12.11", "@swc/core-win32-x64-msvc": "1.12.11" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-P3GM+0lqjFctcp5HhR9mOcvLSX3SptI9L1aux0Fuvgt8oH4f92rCUrkodAa0U2ktmdjcyIiG37xg2mb/dSCYSA=="], + + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw=="], + + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-PTuUQrfStQ6cjW+uprGO2lpQHy84/l0v+GqRqq8s/jdK55rFRjMfCeyf6FAR0l6saO5oNOQl+zWR1aNpj8pMQw=="], + + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.11", "", { "os": "linux", "cpu": "arm" }, "sha512-poxBq152HsupOtnZilenvHmxZ9a8SRj4LtfxUnkMDNOGrZR9oxbQNwEzNKfi3RXEcXz+P8c0Rai1ubBazXv8oQ=="], + + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-y1HNamR/D0Hc8xIE910ysyLe269UYiGaQPoLjQS0phzWFfWdMj9bHM++oydVXZ4RSWycO7KyJ3uvw4NilvyMKQ=="], + + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-LlBxPh/32pyQsu2emMEOFRm7poEFLsw12Y1mPY7FWZiZeptomKSOSHRzKDz9EolMiV4qhK1caP1lvW4vminYgQ=="], + + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.11", "", { "os": "linux", "cpu": "x64" }, "sha512-bOjiZB8O/1AzHkzjge1jqX62HGRIpOHqFUrGPfAln/NC6NR+Z2A78u3ixV7k5KesWZFhCV0YVGJL+qToL27myA=="], + + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.11", "", { "os": "linux", "cpu": "x64" }, "sha512-4dzAtbT/m3/UjF045+33gLiHd8aSXJDoqof7gTtu4q0ZyAf7XJ3HHspz+/AvOJLVo4FHHdFcdXhmo/zi1nFn8A=="], + + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-h8HiwBZErKvCAmjW92JvQp0iOqm6bncU4ac5jxBGkRApabpUenNJcj3h2g5O6GL5K6T9/WhnXE5gyq/s1fhPQg=="], + + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-1pwr325mXRNUhxTtXmx1IokV5SiRL+6iDvnt3FRXj+X5UvXXKtg2zeyftk+03u8v8v8WUr5I32hIypVJPTNxNg=="], + + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.11", "", { "os": "win32", "cpu": "x64" }, "sha512-5gggWo690Gvs7XiPxAmb5tHwzB9RTVXUV7AWoGb6bmyUd1OXYaebQF0HAOtade5jIoNhfQMQJ7QReRgt/d2jAA=="], + + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + + "@swc/types": ["@swc/types@0.1.23", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], + + "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="], + + "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], + + "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], + + "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="], + + "@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.36.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/type-utils": "8.36.0", "@typescript-eslint/utils": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.36.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.36.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/typescript-estree": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.36.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.36.0", "@typescript-eslint/types": "^8.36.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.36.0", "", { "dependencies": { "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0" } }, "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.36.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.36.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.36.0", "@typescript-eslint/utils": "8.36.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.36.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.36.0", "@typescript-eslint/tsconfig-utils": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.36.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/typescript-estree": "8.36.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.36.0", "", { "dependencies": { "@typescript-eslint/types": "8.36.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA=="], + + "@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@3.10.2", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.11", "@swc/core": "^1.11.31" }, "peerDependencies": { "vite": "^4 || ^5 || ^6 || ^7.0.0-beta.0" } }, "sha512-xD3Rdvrt5LgANug7WekBn1KhcvLn1H3jNBfJRL3reeOIua/WnZOEV5qi5qIBq5T8R0jUDmRtxuvk4bPhzGHDWw=="], + + "acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "birecord": ["birecord@0.1.1", "", {}, "sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="], + + "cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], + + "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], + + "esbuild": ["esbuild@0.25.6", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.6", "@esbuild/android-arm": "0.25.6", "@esbuild/android-arm64": "0.25.6", "@esbuild/android-x64": "0.25.6", "@esbuild/darwin-arm64": "0.25.6", "@esbuild/darwin-x64": "0.25.6", "@esbuild/freebsd-arm64": "0.25.6", "@esbuild/freebsd-x64": "0.25.6", "@esbuild/linux-arm": "0.25.6", "@esbuild/linux-arm64": "0.25.6", "@esbuild/linux-ia32": "0.25.6", "@esbuild/linux-loong64": "0.25.6", "@esbuild/linux-mips64el": "0.25.6", "@esbuild/linux-ppc64": "0.25.6", "@esbuild/linux-riscv64": "0.25.6", "@esbuild/linux-s390x": "0.25.6", "@esbuild/linux-x64": "0.25.6", "@esbuild/netbsd-arm64": "0.25.6", "@esbuild/netbsd-x64": "0.25.6", "@esbuild/openbsd-arm64": "0.25.6", "@esbuild/openbsd-x64": "0.25.6", "@esbuild/openharmony-arm64": "0.25.6", "@esbuild/sunos-x64": "0.25.6", "@esbuild/win32-arm64": "0.25.6", "@esbuild/win32-ia32": "0.25.6", "@esbuild/win32-x64": "0.25.6" }, "bin": "bin/esbuild" }, "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.30.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.30.1", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.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", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": "bin/eslint.js" }, "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ=="], + + "eslint-plugin-react-dom": ["eslint-plugin-react-dom@1.52.2", "", { "dependencies": { "@eslint-react/ast": "1.52.2", "@eslint-react/core": "1.52.2", "@eslint-react/eff": "1.52.2", "@eslint-react/kit": "1.52.2", "@eslint-react/shared": "1.52.2", "@eslint-react/var": "1.52.2", "@typescript-eslint/scope-manager": "^8.34.0", "@typescript-eslint/types": "^8.34.0", "@typescript-eslint/utils": "^8.34.0", "compare-versions": "^6.1.1", "string-ts": "^2.2.1", "ts-pattern": "^5.7.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": "^4.9.5 || ^5.3.3" } }, "sha512-HDwQTwGfJTFAa4x0Bf9NH/TVHULEFjI0/vBNhkZt7JAHFb7v+SrhlXGUIIKfQTPHHJIAQZm8v3yzc5g/NlCokA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], + + "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.20", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA=="], + + "eslint-plugin-react-x": ["eslint-plugin-react-x@1.52.2", "", { "dependencies": { "@eslint-react/ast": "1.52.2", "@eslint-react/core": "1.52.2", "@eslint-react/eff": "1.52.2", "@eslint-react/kit": "1.52.2", "@eslint-react/shared": "1.52.2", "@eslint-react/var": "1.52.2", "@typescript-eslint/scope-manager": "^8.34.0", "@typescript-eslint/type-utils": "^8.34.0", "@typescript-eslint/types": "^8.34.0", "@typescript-eslint/utils": "^8.34.0", "compare-versions": "^6.1.1", "is-immutable-type": "^5.0.1", "string-ts": "^2.2.1", "ts-pattern": "^5.7.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "ts-api-utils": "^2.1.0", "typescript": "^4.9.5 || ^5.3.3" } }, "sha512-Pxpf3YxCUcNgzJVT6blAJ2KvLX32pUxtXndaCZoTdiytFw/H9OZKq4Qczxx/Lpo9Ri5rm4FbIZL3BfL/HGmzBw=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.3.0", "", {}, "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "immer": ["immer@10.1.1", "", {}, "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-immutable-type": ["is-immutable-type@5.0.1", "", { "dependencies": { "@typescript-eslint/type-utils": "^8.0.0", "ts-api-utils": "^2.0.0", "ts-declaration-location": "^1.0.4" }, "peerDependencies": { "eslint": "*", "typescript": ">=4.7.4" } }, "sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": "bin/jsesc" }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "optionator": ["optionator@0.9.4", "", { "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" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.6.2", "", { "bin": "bin/prettier.cjs" }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], + + "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], + + "react-is": ["react-is@19.1.0", "", {}, "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg=="], + + "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], + + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rollup": ["rollup@4.44.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.2", "@rollup/rollup-android-arm64": "4.44.2", "@rollup/rollup-darwin-arm64": "4.44.2", "@rollup/rollup-darwin-x64": "4.44.2", "@rollup/rollup-freebsd-arm64": "4.44.2", "@rollup/rollup-freebsd-x64": "4.44.2", "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", "@rollup/rollup-linux-arm-musleabihf": "4.44.2", "@rollup/rollup-linux-arm64-gnu": "4.44.2", "@rollup/rollup-linux-arm64-musl": "4.44.2", "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-musl": "4.44.2", "@rollup/rollup-linux-s390x-gnu": "4.44.2", "@rollup/rollup-linux-x64-gnu": "4.44.2", "@rollup/rollup-linux-x64-musl": "4.44.2", "@rollup/rollup-win32-arm64-msvc": "4.44.2", "@rollup/rollup-win32-ia32-msvc": "4.44.2", "@rollup/rollup-win32-x64-msvc": "4.44.2", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + + "semver": ["semver@7.7.2", "", { "bin": "bin/semver.js" }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "string-ts": ["string-ts@2.2.1", "", {}, "sha512-Q2u0gko67PLLhbte5HmPfdOjNvUKbKQM+mCNQae6jE91DmoFHY6HH9GcdqCeNx87DZ2KKjiFxmA0R/42OneGWw=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "stylis": ["stylis@4.2.0", "", {}, "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "ts-declaration-location": ["ts-declaration-location@1.0.7", "", { "dependencies": { "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": ">=4.0.0" } }, "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA=="], + + "ts-pattern": ["ts-pattern@5.7.1", "", {}, "sha512-EGs8PguQqAAUIcQfK4E9xdXxB6s2GK4sJfT/vcc9V1ELIvC4LH/zXu2t/5fajtv6oiRCxdv7BgtVK3vWgROxag=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "typescript-eslint": ["typescript-eslint@8.36.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.36.0", "@typescript-eslint/parser": "8.36.0", "@typescript-eslint/utils": "8.36.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA=="], + + "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-immer": ["use-immer@0.11.0", "", { "peerDependencies": { "immer": ">=8.0.0", "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0" } }, "sha512-RNAqi3GqsWJ4bcCd4LMBgdzvPmTABam24DUaFiKfX9s3MSorNRz9RDZYJkllJoMHUxVLMDetwAuCDeyWNrp1yA=="], + + "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx"], "bin": "bin/vite.js" }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "yaml": ["yaml@2.8.0", "", { "bin": "bin.mjs" }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.15.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA=="], + + "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "cosmiconfig/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "tinyglobby/fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], + + "tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "ts-declaration-location/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + } +} diff --git a/exporter/SynthesisFusionAddin/web/index.html b/exporter/SynthesisFusionAddin/web/index.html new file mode 100644 index 0000000000..0429768457 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/index.html @@ -0,0 +1,12 @@ + + + + + + Synthesis Exporter + + +
+ + + diff --git a/exporter/SynthesisFusionAddin/web/package.json b/exporter/SynthesisFusionAddin/web/package.json new file mode 100644 index 0000000000..629b8352c4 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/package.json @@ -0,0 +1,45 @@ +{ + "name": "web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "NODE_ENV=dev vite build --watch", + "dev:ui": "vite --open", + "build": "vite build", + "lint": "biome lint", + "lint:fix": "biome lint --fix", + "style": "biome format", + "style:fix": "biome format --fix", + "preview": "vite preview", + "format": "biome check --fix" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.2.0", + "@mui/material": "^7.2.0", + "eslint-plugin-react-dom": "^1.52.2", + "eslint-plugin-react-x": "^1.52.2", + "immer": "^10.1.1", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "use-immer": "^0.11.0" + }, + "devDependencies": { + "@biomejs/biome": "2.1.1", + "@eslint/js": "^9.29.0", + "@types/node": "^24.0.14", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react-swc": "^3.10.2", + "eslint": "^9.29.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.2.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.34.1", + "vite": "^6.3.5", + "prettier": "^3.6.2" + } +} diff --git a/exporter/SynthesisFusionAddin/web/src/App.css b/exporter/SynthesisFusionAddin/web/src/App.css new file mode 100644 index 0000000000..e407e09dd2 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/App.css @@ -0,0 +1,45 @@ +#root { + /*max-width: 1280px;*/ + width: 100vw; + height: 100vh; + margin: 0 auto; + /*padding: 2rem;*/ + text-align: center; + box-sizing: border-box; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/exporter/SynthesisFusionAddin/web/src/App.tsx b/exporter/SynthesisFusionAddin/web/src/App.tsx new file mode 100644 index 0000000000..c77a825651 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/App.tsx @@ -0,0 +1,312 @@ +import { useCallback, useEffect, useState } from "react" + +import "./App.css" +import { RestartAlt, Settings, SportsFootball, Texture } from "@mui/icons-material" +import DownloadIcon from "@mui/icons-material/Download" +import PrecisionManufacturingIcon from "@mui/icons-material/PrecisionManufacturing" +import SaveIcon from "@mui/icons-material/Save" +import { + AppBar, + Backdrop, + Box, + Button, + CircularProgress, + Container, + Stack, + Tab, + Tabs, + ThemeProvider, +} from "@mui/material" +import { current } from "immer" +import { useImmer } from "use-immer" +import { sendData, sendDataAndToast } from "./lib" +import { Global_SetAlert } from "./lib/GlobalUtils.tsx" +import { createJoint } from "./lib/joints.ts" +import { theme } from "./lib/theme.ts" +import { + DefaultExporterConfig, + type ExporterConfig, + ExportMode, + type Gamepiece, + type GeneralConfig, + type Joint, + WheelType, +} from "./lib/types.ts" +import GamepiecesConfigTab from "./ui/GamepiecesConfigTab.tsx" +import GeneralConfigTab from "./ui/GeneralConfigTab.tsx" +import GlobalAlert from "./ui/GlobalAlert.tsx" +import JointsConfigTab from "./ui/JointsConfigTab.tsx" +import MaterialTaggingTab, { type TaggedBody } from "./ui/MaterialTaggingTab.tsx" + +function TabPanel(props: { children?: React.ReactNode; value: number; index: number }) { + const { children, value, index, ...other } = props + + return ( + + ) +} + +function App() { + const [activeTab, setActiveTab] = useState(0) + const [generalConfig, updateGeneralConfig] = useImmer(DefaultExporterConfig()) + const [joints, updateJoints] = useImmer([]) + const [gamepieces, updateGamepieces] = useImmer([]) + const [taggedBodies, updateTaggedBodies] = useImmer([]) + const [isSelecting, setIsSelecting] = useState(false) + const updateConfigItem = (k: K, v: GeneralConfig[K]) => { + updateGeneralConfig(config => { + config[k] = v + }) + } + + const loadConfigFromFusion = useCallback(() => { + if (typeof window.adsk === "undefined") { + requestAnimationFrame(loadConfigFromFusion) + return + } + sendData("init", {}) + .then(data => { + if (data === undefined) { + Global_SetAlert("error", "Could not extract data from fusion") + return + } + console.log(data) + updateGeneralConfig(config => { + const entries = Object.entries(data.options) + entries.forEach(([k, v]) => { + if (k in config) { + config[k as "robotWeight"] = v as number // Object.entries is terribly typed + } + }) + config.calculatedRobotWeight = data.calculatedMass + config.robotWeight = config.autoCalcRobotWeight ? data.calculatedMass : config.robotWeight + }) + updateJoints(() => { + if (data.options.joints.length === 0) { + return data.jointData.map(fusionJoint => createJoint(fusionJoint)) + } + const res: Joint[] = data.options.joints + .map(joint => { + const wheel = joint.isWheel + ? data.options.wheels.find(wheel => wheel.jointToken === joint.jointToken) + : undefined + const fusionJoint = data.jointData.find(j => j.entityToken === joint.jointToken) + if (fusionJoint === undefined) { + return null // No longer in the assembly + } + return { + ...joint, + wheelType: wheel?.wheelType ?? WheelType.STANDARD, + name: fusionJoint.name, + type: fusionJoint.jointType, + } + }) + .filter(e => e != null) + return res + }) + updateGamepieces(() => { + const res: Gamepiece[] = data.options.gamepieces + .map(gamepiece => { + const fusionGamepiece = data.gamepieceData.find( + g => g.occurrenceToken === gamepiece.occurrenceToken + ) + if (fusionGamepiece === undefined) { + return null // No longer in the assembly + } + return { + occurrenceToken: gamepiece.occurrenceToken, + userDefinedMass: gamepiece.weight, + friction: gamepiece.friction, + calculatedMass: fusionGamepiece.mass, + name: fusionGamepiece.name, + entityIDs: fusionGamepiece.entityIDs, + } + }) + .filter(e => e != null) + return res + }) + + updateTaggedBodies(() => { + const res: TaggedBody[] = Object.entries(data.options.tags) + .map(([key, value]) => { + const fusionBody = data.tagData.find(b => b.entityToken === key) + if (fusionBody === undefined) { + return null // No longer in the assembly + } + return { + ...fusionBody, + material: value, + } + }) + .filter(e => e != null) + return res + }) + }) + .catch((e: unknown) => { + console.error(e) + Global_SetAlert("error", "Could not load config") + }) + }, [updateGamepieces, updateJoints, updateGeneralConfig, updateTaggedBodies]) + + useEffect(() => { + loadConfigFromFusion() + }, [loadConfigFromFusion]) + + const getFinalizedConfig = () => + new Promise(resolve => { + updateGeneralConfig(cfg => { + cfg.gamepieces = gamepieces.map(gamepiece => ({ + occurrenceToken: gamepiece.occurrenceToken, + weight: generalConfig.autoCalcGamepieceWeight + ? gamepiece.calculatedMass + : gamepiece.userDefinedMass, + friction: gamepiece.friction, + })) + cfg.joints = joints + cfg.wheels = joints + .filter(joint => joint.isWheel) + .map(joint => ({ + jointToken: joint.jointToken, + signalType: joint.signalType, + wheelType: joint.wheelType, + })) + cfg.tags = Object.fromEntries(taggedBodies.map(b => [b.entityToken, b.material])) + resolve(current(cfg)) + }) + }) + return ( + + ({ color: "#fff", zIndex: theme.zIndex.drawer + 1, backdropFilter: "blur(0px)" })} + open={isSelecting} + onClick={() => { + Global_SetAlert( + "info", + "Click on an element in the Fusion assembly, or press escape in the main Fusion window" + ) + }} + > + + ({ + paddingX: "0.5rem", + paddingY: "0.4rem", + borderRadius: "0.5rem", + backgroundColor: theme.palette.grey.A700, + boxShadow: theme.shadows[5], + })} + > + Click on an element in the Fusion assembly + + + + + + + + { + setActiveTab(v as number) + }} + > + } iconPosition={"start"} label="General" /> + } + iconPosition={"start"} + label="Joints" + disabled={generalConfig.exportMode === ExportMode.FIELD} + /> + } + iconPosition={"start"} + label="Gamepieces" + disabled={generalConfig.exportMode === ExportMode.ROBOT} + /> + + } iconPosition={"start"} label="Materials" /> + + {/**/} + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default App diff --git a/exporter/SynthesisFusionAddin/web/src/index.css b/exporter/SynthesisFusionAddin/web/src/index.css new file mode 100644 index 0000000000..18f7d16ccd --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/index.css @@ -0,0 +1,75 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +html { + overflow: hidden; + height: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 100vw; + min-height: 100vh; + height: 100%; + overflow: auto; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/exporter/SynthesisFusionAddin/web/src/lib/GlobalUtils.tsx b/exporter/SynthesisFusionAddin/web/src/lib/GlobalUtils.tsx new file mode 100644 index 0000000000..6c6dca2133 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/lib/GlobalUtils.tsx @@ -0,0 +1,9 @@ +import type { AlertColor } from "@mui/material" + +export let Global_SetAlert: (type: AlertColor, text: string) => void = () => { + console.warn("unoverriden alert called") +} + +export function setGlobalSetAlert(func: typeof Global_SetAlert) { + Global_SetAlert = func +} diff --git a/exporter/SynthesisFusionAddin/web/src/lib/index.ts b/exporter/SynthesisFusionAddin/web/src/lib/index.ts new file mode 100644 index 0000000000..713e244639 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/lib/index.ts @@ -0,0 +1,148 @@ +import { Global_SetAlert } from "./GlobalUtils.tsx" +import type { FusionJoint } from "./joints.ts" +import type { ExporterConfig } from "./types.ts" + +declare global { + interface Window { + adsk: { + fusionSendData(action: string, body: string): Promise + } + fusionJavaScriptHandler: { + /* The return value is a string and is passed back to your add-in as the return argument of the sendInfoToHTML method. Returning an empty string is interpreted as an error, so you should always return something in both success and failure cases.*/ + handle(action: string, body: string): string + } + } +} + +type Empty = Record + +interface InitResponse { + calculatedMass: number + options: ExporterConfig + jointData: FusionJoint[] + gamepieceData: FusionGamepiece[] + tagData: FusionBody[] +} + +interface Messages { + selectJoint: [Empty, FusionJoint] + selectGamepiece: [Empty, FusionGamepiece[]] + selectBody: [Empty, FusionBody] + export: [ExporterConfig, Empty] + save: [ExporterConfig, Empty] + init: [Empty, InitResponse] +} + +const errorMatchers: { text: string; cb: () => void }[] = [ + { + text: "selections.size() > 0", + cb: () => { + Global_SetAlert("info", "Selection cancelled") + }, + }, +] + +export async function sendData( + action: A, + body: Messages[A][0] +): Promise<(Messages[A][1] & { _err?: string }) | undefined> { + console.log({ action, body: JSON.stringify(body) }) + const resp = await window.adsk.fusionSendData(action, JSON.stringify(body)) + if (resp === "") { + Global_SetAlert("error", "Fusion did not respond. Try restarting the application") + return undefined + } + try { + const parsed = JSON.parse(resp) as Messages[A][1] & { _err?: string } + if (parsed._err !== undefined) { + const wasHandled = errorMatchers.some(matcher => { + if (parsed._err?.includes(matcher.text)) { + matcher.cb() + return true + } + return false + }) + if (!wasHandled) { + console.error({ action, errorResponse: parsed._err }) + } + + return undefined + } + return parsed as Messages[A][1] + } catch (error) { + console.error({ error, resp }) + return undefined + } +} + +export async function sendDataAndToast( + action: A, + body: Messages[A][0] & object, + sucessMsg: string, + failureMsg = "Fusion did not respond. Try restarting the application" +): Promise { + const resp = await sendData(action, body) + + if (resp === undefined) { + Global_SetAlert("error", failureMsg) + } else { + Global_SetAlert("info", sucessMsg) + } + return resp +} + +export interface FusionGamepiece { + name: string + occurrenceToken: string + mass: number + entityIDs: string[] +} +export async function selectGamepiece(): Promise { + if (import.meta.env.DEV && typeof window.adsk === "undefined") { + return new Promise(resolve => { + setTimeout(() => { + const token = Math.random().toString(36).substring(2, 15) + resolve([ + { + occurrenceToken: `${token}_${Math.random().toString(36).substring(2, 15)}`, + name: `Component ${token.substring(0, 2).toUpperCase()}`, + mass: Math.round(Math.random() * 100) / 10, + entityIDs: [ + token, + Math.random().toString(36).substring(2, 15), + Math.random().toString(36).substring(2, 15), + ], + }, + ]) + }, 2000) + }) + } + return await sendData("selectGamepiece", {}) +} + +export interface FusionBody { + entityToken: string + name: string + componentName: string +} +export async function selectBody(): Promise { + if (import.meta.env.DEV && typeof window.adsk === "undefined") { + return new Promise(resolve => { + setTimeout(() => { + const token = Math.random().toString(36).substring(2, 15) + resolve({ + entityToken: token, + name: `Body ${token.substring(0, 2).toUpperCase()}`, + componentName: `Component ${token.substring(2, 3).toUpperCase()}`, + }) + }, 2000) + }) + } + return await sendData("selectBody", {}) +} +window.fusionJavaScriptHandler = { + handle: (action, data) => { + console.log({ action, data }) + return "OK" + }, +} diff --git a/exporter/SynthesisFusionAddin/web/src/lib/joints.ts b/exporter/SynthesisFusionAddin/web/src/lib/joints.ts new file mode 100644 index 0000000000..c2d41d8eb6 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/lib/joints.ts @@ -0,0 +1,64 @@ +import revoluteIcon from "../../../src/Resources/JointIcons/JointRev/32x32.png" +import sliderIcon from "../../../src/Resources/JointIcons/JointSlider/32x32.png" +import { sendData } from "./index.ts" +import { type Joint, JointParentType, JointType, SignalType, WheelType } from "./types.ts" + +export const jointInfo: Partial< + Record +> = { + [JointType.RevoluteJointType]: { + icon: revoluteIcon, + name: "Revolute", + defaultSpeed: Math.PI, + speedUnits: "rad/s", + }, + [JointType.SliderJointType]: { + icon: sliderIcon, + name: "Slider", + defaultSpeed: 100, + speedUnits: "cm/s", + }, +} + +export const signalInfo: Record = { + [SignalType.PWM]: { bg: "#ffa779", outline: "#ff894a", fg: "#ce5d21" }, + [SignalType.CAN]: { bg: "#79ff84", outline: "#77ff4a", fg: "#00bb19" }, + [SignalType.PASSIVE]: { bg: "#cbcbcb", outline: "#8d8d8d", fg: "#5d5d5d" }, +} + +export function createJoint(fusionJoint: FusionJoint): Joint { + return { + entityToken: fusionJoint.entityToken, + name: fusionJoint.name, + type: fusionJoint.jointType, + parent: JointParentType.ROOT, + signalType: SignalType.PWM, + speed: jointInfo[fusionJoint.jointType]?.defaultSpeed ?? 0, + force: 0.05, + isWheel: false, + wheelType: WheelType.STANDARD, + } +} + +export interface FusionJoint { + entityToken: string + name: string + jointType: JointType +} + +export async function selectJoint(): Promise { + if (import.meta.env.DEV && typeof window.adsk === "undefined") { + return new Promise(resolve => { + setTimeout(() => { + const jointType = Math.round(1 + Math.random()) + const token = Math.random().toString(36).substring(2, 15) + resolve({ + entityToken: token, + name: `${jointType === 1 ? "Revolute" : "Slider"} ${token.substring(0, 2).toUpperCase()}`, + jointType: jointType, + }) + }, 2000) + }) + } + return await sendData("selectJoint", {}) +} diff --git a/exporter/SynthesisFusionAddin/web/src/lib/theme.ts b/exporter/SynthesisFusionAddin/web/src/lib/theme.ts new file mode 100644 index 0000000000..bfcbb4e24e --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/lib/theme.ts @@ -0,0 +1,76 @@ +import { createTheme } from "@mui/material" + +export const theme = createTheme({ + components: { + MuiButton: { + defaultProps: { + size: "small", + }, + }, + MuiFilledInput: { + defaultProps: { + margin: "dense", + }, + }, + MuiFormControl: { + defaultProps: { + margin: "dense", + }, + }, + MuiFormHelperText: { + defaultProps: { + margin: "dense", + }, + }, + MuiSelect: { + defaultProps: { + size: "small", + }, + }, + MuiIconButton: { + defaultProps: { + size: "small", + }, + }, + MuiInputBase: { + defaultProps: { + margin: "dense", + }, + }, + MuiInputLabel: { + defaultProps: { + margin: "dense", + }, + }, + MuiListItem: { + defaultProps: { + dense: true, + }, + }, + MuiOutlinedInput: { + defaultProps: { + margin: "dense", + }, + }, + MuiFab: { + defaultProps: { + size: "small", + }, + }, + MuiTable: { + defaultProps: { + size: "small", + }, + }, + MuiTextField: { + defaultProps: { + margin: "dense", + }, + }, + MuiToolbar: { + defaultProps: { + variant: "dense", + }, + }, + }, +}) diff --git a/exporter/SynthesisFusionAddin/web/src/lib/types.ts b/exporter/SynthesisFusionAddin/web/src/lib/types.ts new file mode 100644 index 0000000000..d311fd4bb1 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/lib/types.ts @@ -0,0 +1,218 @@ +import type { Material } from "../ui/MaterialTaggingTab.tsx" + +export type GeneralConfig = ExporterConfig & { calculatedRobotWeight: number } + +export function DefaultExporterConfig(): GeneralConfig { + return { + fileLocation: "", + + gamepieces: [], + joints: [], + wheels: [], + tags: {}, + + materials: 0, + exportMode: ExportMode.ROBOT, + exportLocation: ExportLocation.DOWNLOAD, + autoCalcRobotWeight: true, + robotWeight: 0, + compressOutput: true, + exportAsPart: false, + frictionOverride: false, + frictionOverrideCoeff: 0.5, + autoCalcGamepieceWeight: true, + openSynthesisUponExport: false, + + calculatedRobotWeight: 0, + + hierarchy: ModelHierarchy.FusionAssembly, + physicalCalculationLevel: CalculationAccuracy.LowCalculationAccuracy, + physicalDepth: PhysicalDepth.AllOccurrence, + visualQuality: TriangleMeshQualityOptions.LowQualityTriangleMesh, + } +} +export interface Gamepiece { + occurrenceToken: string + name: string + userDefinedMass: number + calculatedMass: number + entityIDs: string[] + friction: number +} + +export interface Joint { + jointToken: string + name: string + type: JointType + parent: JointParentType + signalType: SignalType + speed: number + force: number + isWheel: boolean + wheelType: WheelType +} + +export enum ExportLocation { + UPLOAD = 1, + DOWNLOAD, +} + +export enum ExportMode { + ROBOT = 1, + FIELD, +} +export enum WheelType { + STANDARD = 1, + OMNI, + MECANUM, +} + +export enum CalculationAccuracy { + LowCalculationAccuracy = 0, + MediumCalculationAccuracy = 1, + HighCalculationAccuracy = 2, + VeryHighCalculationAccuracy = 3, +} + +export enum TriangleMeshQualityOptions { + LowQualityTriangleMesh = 8, + + NormalQualityTriangleMesh = 11, + HighQualityTriangleMesh = 13, + VeryHighQualityTriangleMesh = 15, +} + +enum PhysicalDepth { + // No Physical Properties are generated + NoPhysical = 0, + + // Only Body Physical Objects are generated + Body = 1, + + // Only Occurrence that contain Bodies and Bodies have Physical Properties + SurfaceOccurrence = 2, + + // Every Single Occurrence has Physical Properties even if empty + AllOccurrence = 3, +} + +enum ModelHierarchy { + // Model exactly as it is shown in Fusion in the model view tree + FusionAssembly = 0, + + // Flattened Assembly with all bodies as children of the root object + FlatAssembly = 1, + + // A Model represented with parented objects that are part of a jointed tree + PhysicalAssembly = 2, + + // Generates the root assembly as a single mesh and stores the associated data + SingleMesh = 3, +} + +export enum SignalType { + PWM = 1, + CAN, + PASSIVE, +} +export enum JointType { + RigidJointType, + RevoluteJointType, + SliderJointType, + CylindricalJointType, + PinSlotJointType, + PlanarJointType, + BallJointType, +} + +export enum JointParentType { + ROOT = 1, + END, +} + +export type SelectionFilter = + | "Bodies" + | "SolidBodies" + | "SurfaceBodies" + | "MeshBodies" + | "Faces" + | "SolidFaces" + | "SurfaceFaces" + | "PlanarFaces" + | "CylindricalFaces" + | "ConicalFaces" + | "SphericalFaces" + | "ToroidalFaces" + | "SplineFaces" + | "Edges" + | "LinearEdges" + | "CircularEdges" + | "EllipticalEdges" + | "TangentEdges" + | "NonTangentEdges" + | "Vertices" + | "RootComponents" + | "Occurrences" + | "Sketches" + | "SketchCurves" + | "SketchLines" + | "SketchCircles" + | "SketchPoints" + | "ConstructionPoints" + | "ConstructionLines" + | "ConstructionPlanes" + | "Features" + | "Canvases" + | "Decals" + | "JointOrigins" + | "Joints" + | "SketchConstraints" + | "Profiles" + | "Texts" + | "CustomGraphics" + +export interface ExporterConfig { + fileLocation: string + name?: string + version?: string + materials: number + exportMode: ExportMode + wheels: ExporterWheel[] + joints: ExporterJoint[] + tags: Record + gamepieces: ExporterGamepiece[] + robotWeight: number + autoCalcRobotWeight: boolean + autoCalcGamepieceWeight: boolean + frictionOverride: boolean + frictionOverrideCoeff: number + compressOutput: boolean + exportAsPart: boolean + exportLocation: ExportLocation + openSynthesisUponExport: boolean + hierarchy: ModelHierarchy + visualQuality: TriangleMeshQualityOptions + physicalDepth: PhysicalDepth + physicalCalculationLevel: CalculationAccuracy +} + +interface ExporterJoint { + jointToken: string + parent: JointParentType + signalType: SignalType + speed: number + force: number + isWheel: boolean +} + +interface ExporterGamepiece { + occurrenceToken: string + weight: number + friction: number +} + +interface ExporterWheel { + jointToken: string + wheelType: WheelType + signalType: SignalType +} diff --git a/exporter/SynthesisFusionAddin/web/src/main.tsx b/exporter/SynthesisFusionAddin/web/src/main.tsx new file mode 100644 index 0000000000..8f873d7322 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/main.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from "react" +import { createRoot } from "react-dom/client" +import "./index.css" +import App from "./App.tsx" + +// biome-ignore lint/style/noNonNullAssertion: can't do anything if it's not there +createRoot(document.getElementById("root")!).render( + + + +) diff --git a/exporter/SynthesisFusionAddin/web/src/ui/GamepiecesConfigTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/GamepiecesConfigTab.tsx new file mode 100644 index 0000000000..65c6f8654e --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/ui/GamepiecesConfigTab.tsx @@ -0,0 +1,205 @@ +import BalanceIcon from "@mui/icons-material/Balance" +import DeleteIcon from "@mui/icons-material/Delete" +import { + Box, + IconButton, + InputAdornment, + List, + ListItem, + ListItemIcon, + ListItemText, + Paper, + Slider, + Switch, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, +} from "@mui/material" +import { selectGamepiece } from "../lib" +import { Global_SetAlert } from "../lib/GlobalUtils.tsx" +import type { Gamepiece, GeneralConfig } from "../lib/types" +import FusionSelectButton from "./components/SelectButton.tsx" + +interface GamepiecesConfigTabProps { + gamepieces: Gamepiece[] + updateGamepieces: (cb: (gamepieces: Gamepiece[]) => void) => void + config: GeneralConfig + updateConfigItem: (key: K, value: GeneralConfig[K]) => void + selection: { + isSelecting: boolean + setIsSelecting: (value: boolean) => void + } +} + +function GamepiecesConfigTab({ + gamepieces, + updateGamepieces, + config, + updateConfigItem, + selection, +}: GamepiecesConfigTabProps) { + function updateItem(index: number, key: K, value: Gamepiece[K]) { + updateGamepieces(items => { + items[index][key] = value + }) + } + return ( + <> + + + + + + + { + updateConfigItem("autoCalcGamepieceWeight", v) + }} + /> + + +

+ {gamepieces.length} Gamepiece{gamepieces.length === 1 ? "" : "s"} +

+ + + + + + Name + + + Weight (kg) + + + Friction + + + + + + {gamepieces.map((gamepiece, i) => ( + + {gamepiece.name} + + kg, + }, + }} + value={ + config.autoCalcGamepieceWeight + ? gamepiece.calculatedMass + : gamepiece.userDefinedMass + } + onInput={e => { + updateItem( + i, + "userDefinedMass", + parseFloat((e.target as HTMLInputElement).value) || 0 + ) + }} + /> + + + + { + updateItem(i, "friction", v) + }} + sx={{ flexBasis: "80%" }} + value={gamepiece.friction} + /> + { + updateItem( + i, + "friction", + parseFloat((e.target as HTMLInputElement).value) || 0 + ) + }} + /> + + + + { + updateGamepieces(draft => { + draft.splice(i, 1) + }) + }} + > + + + + + ))} + +
+
+ { + if (data == null) return + const allDuplicates = data.every(newgamepiece => { + if (gamepieces.some(gamepiece => gamepiece.entityIDs.includes(newgamepiece.entityIDs[0]))) { + console.warn("attempted to add existing element") + Global_SetAlert("warning", "Component already added") + return true + } + return false + }) + if (allDuplicates) { + return + } + updateGamepieces(draft => { + data.forEach(gamepiece => { + const roundedMass = Math.round(gamepiece.mass * 100) / 100 + draft.push({ + ...gamepiece, + userDefinedMass: roundedMass, + calculatedMass: roundedMass, + friction: 0.5, + }) + }) + }) + }} + /> + + ) +} + +export default GamepiecesConfigTab diff --git a/exporter/SynthesisFusionAddin/web/src/ui/GeneralConfigTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/GeneralConfigTab.tsx new file mode 100644 index 0000000000..13536cac5b --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/ui/GeneralConfigTab.tsx @@ -0,0 +1,221 @@ +import AnimationIcon from "@mui/icons-material/Animation" +import ArchiveIcon from "@mui/icons-material/Archive" +import BalanceIcon from "@mui/icons-material/Balance" +import LaunchIcon from "@mui/icons-material/Launch" +import PrecisionManufacturingIcon from "@mui/icons-material/PrecisionManufacturing" +import SaveIcon from "@mui/icons-material/Save" +import TuneIcon from "@mui/icons-material/Tune" +import { + Box, + Collapse, + InputAdornment, + List, + ListItem, + ListItemIcon, + ListItemText, + MenuItem, + Select, + Slider, + Switch, + TextField, +} from "@mui/material" +import type * as React from "react" +import type { ChangeEvent } from "react" +import { ExportLocation, ExportMode, type GeneralConfig } from "../lib/types.ts" + +interface ConfigTabProps { + config: GeneralConfig + updateConfigItem: (key: K, value: GeneralConfig[K]) => void +} +function GeneralConfigTab({ config, updateConfigItem }: ConfigTabProps): React.ReactElement { + function updateSelect(key: K) { + return ( + e: + | ChangeEvent & { value: GeneralConfig[K] }> + | (Event & { target: { value: GeneralConfig[K] } }) + ) => { + updateConfigItem(key, e.target.value) + } + } + + function updateLiteral(key: K) { + return (_: Event | ChangeEvent, v: GeneralConfig[K]) => { + updateConfigItem(key, v) + } + } + + return ( + + + + + + + + + + + + + + + + + + + + + + + { + updateConfigItem("autoCalcRobotWeight", v) + if (v) { + updateConfigItem("robotWeight", config.calculatedRobotWeight) + } + }} + checked={config.autoCalcRobotWeight} + /> + + + + + kg, + }, + }} + size="small" + style={{ minWidth: "10rem" }} + onChange={e => { + updateConfigItem("robotWeight", safeParseFloat(e.target.value) ?? 0) + }} + value={config.robotWeight} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/**/} + + { + updateConfigItem( + "frictionOverrideCoeff", + safeParseFloat(e.target.value) ?? config.frictionOverrideCoeff + ) + }} + value={config.frictionOverrideCoeff} + /> + + + + + + + + + + + + + ) +} + +function safeParseFloat(value: string | undefined): number | undefined { + if (value === undefined) { + return + } + const parsed = parseFloat(value) + return Number.isNaN(parsed) ? undefined : parsed +} + +export default GeneralConfigTab diff --git a/exporter/SynthesisFusionAddin/web/src/ui/GlobalAlert.tsx b/exporter/SynthesisFusionAddin/web/src/ui/GlobalAlert.tsx new file mode 100644 index 0000000000..78ad18ab15 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/ui/GlobalAlert.tsx @@ -0,0 +1,36 @@ +import { Alert, type AlertColor, Snackbar } from "@mui/material" +import { useState } from "react" +import { setGlobalSetAlert } from "../lib/GlobalUtils.tsx" + +function GlobalAlert() { + const [active, setActive] = useState(false) + const [text, setText] = useState("") + const [severity, setSeverity] = useState("info") + setGlobalSetAlert((severity, text) => { + setSeverity(severity) + setText(text) + setActive(true) + }) + return ( + { + setActive(false) + }} + > + { + setActive(false) + }} + severity={severity} + variant="filled" + > + {text} + + + ) +} + +export default GlobalAlert diff --git a/exporter/SynthesisFusionAddin/web/src/ui/JointsConfigTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/JointsConfigTab.tsx new file mode 100644 index 0000000000..77a38dc53b --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/ui/JointsConfigTab.tsx @@ -0,0 +1,258 @@ +import DeleteIcon from "@mui/icons-material/Delete" +import { + Checkbox, + IconButton, + InputAdornment, + MenuItem, + Paper, + Select, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, +} from "@mui/material" +import { Global_SetAlert } from "../lib/GlobalUtils.tsx" +import { createJoint, jointInfo, selectJoint, signalInfo } from "../lib/joints" +import { type Joint, JointParentType, JointType, SignalType, WheelType } from "../lib/types" +import FusionSelectButton from "./components/SelectButton.tsx" + +interface JointsConfigTabProps { + joints: Joint[] + updateJoints: (cb: (joints: Joint[]) => void) => void + selection: { + isSelecting: boolean + setIsSelecting: (value: boolean) => void + } +} +function JointsConfigTab({ joints, updateJoints, selection }: JointsConfigTabProps) { + function updateJoint(index: number, key: K, value: Joint[K]) { + updateJoints(joints => { + joints[index][key] = value + }) + } + return ( + <> +

+ {joints.length} Joint{joints.length !== 1 ? "s" : ""} +

+ + + + + + Type + + + Name + + + Parent Joint + + + Signal Type + + + Joint Speed + + + Joint Force + + + Is Wheel? + + + + + + {joints.map((row, i) => ( + + + {jointInfo[row.type]?.name} + + {row.name} + + + + + + + + + {jointInfo[row.type]?.speedUnits} + + ), + }, + }} + value={row.speed} + onInput={e => { + updateJoint( + i, + "speed", + parseFloat((e.target as HTMLInputElement).value) || 0 + ) + }} + /> + + + N, + }, + }} + value={row.force} + onInput={e => { + updateJoint( + i, + "force", + parseFloat((e.target as HTMLInputElement).value) || 0 + ) + }} + /> + + + { + updateJoint(i, "isWheel", v) + }} + /> + + + { + updateJoints(joints => { + joints.splice(i, 1) + }) + }} + > + + + + + ))} + +
+
+ { + if (data == null) return + if (joints.some(joint => joint.jointToken === data.entityToken)) { + Global_SetAlert("warning", "Joint already selected") + return + } + updateJoints(draft => { + draft.push(createJoint(data)) + }) + }} + /> + +

+ {joints.filter(j => j.isWheel).length} Wheel{joints.filter(j => j.isWheel).length !== 1 ? "s" : ""} +

+ + + + + + Joint + + + Type + + + + + {joints.map( + (joint, i) => + joint.isWheel && ( + + {/*{jointInfo[row.type]?.name}*/} + {joint.name} + + + + + ) + )} + +
+
+ + ) +} + +export default JointsConfigTab diff --git a/exporter/SynthesisFusionAddin/web/src/ui/MaterialTaggingTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/MaterialTaggingTab.tsx new file mode 100644 index 0000000000..37654ad1eb --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/ui/MaterialTaggingTab.tsx @@ -0,0 +1,119 @@ +import DeleteIcon from "@mui/icons-material/Delete" +import { + IconButton, + MenuItem, + Paper, + Select, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, +} from "@mui/material" + +import { type FusionBody, selectBody } from "../lib" +import { Global_SetAlert } from "../lib/GlobalUtils.tsx" +import FusionSelectButton from "./components/SelectButton.tsx" + +export const MATERIALS = ["Softbody", "Rigid", "Chain", "Spring", "Rope"] as const +export type Material = (typeof MATERIALS)[number] +export type TaggedBody = FusionBody & { + material: Material +} +interface MaterialTaggingTabProps { + tags: TaggedBody[] + updateTags: (cb: (tags: TaggedBody[]) => void) => void + selection: { + isSelecting: boolean + setIsSelecting: (value: boolean) => void + } +} +function MaterialTaggingTab({ tags, updateTags, selection }: MaterialTaggingTabProps) { + function updateTag(index: number, key: K, value: TaggedBody[K]) { + updateTags(items => { + items[index][key] = value + }) + } + return ( + <> +

+ {tags.length} Tagged Item{tags.length === 1 ? "" : "s"} +

+ + + + + + Body Name + + + Component + + + Material + + + + + + {tags.map((tag, i) => ( + + {tag.name} + {tag.componentName} + + + + + { + updateTags(draft => { + draft.splice(i, 1) + }) + }} + > + + + + + ))} + +
+
+ { + if (data == null) return + if (tags.some(tag => tag.entityToken === data.entityToken)) { + console.warn("attempted to add existing element") + Global_SetAlert("warning", "Component already added") + return + } + updateTags(draft => { + draft.push({ + ...data, + material: "Rigid", + }) + }) + }} + /> + + ) +} + +export default MaterialTaggingTab diff --git a/exporter/SynthesisFusionAddin/web/src/ui/components/SelectButton.tsx b/exporter/SynthesisFusionAddin/web/src/ui/components/SelectButton.tsx new file mode 100644 index 0000000000..16b7738a0a --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/ui/components/SelectButton.tsx @@ -0,0 +1,49 @@ +import { Button, Stack } from "@mui/material" +import type { ReactElement } from "react" +import { Global_SetAlert } from "../../lib/GlobalUtils.tsx" + +interface FusionSelectButtonProps { + label: string + onClick: () => T + + onSelection: (data: Awaited) => void + selection: { + isSelecting: boolean + setIsSelecting: (val: boolean) => void + } +} + +const FusionSelectButton = ({ + label, + onClick, + onSelection, + selection, +}: FusionSelectButtonProps): ReactElement => { + return ( + + + + ) +} + +export default FusionSelectButton diff --git a/exporter/SynthesisFusionAddin/web/src/vite-env.d.ts b/exporter/SynthesisFusionAddin/web/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/exporter/SynthesisFusionAddin/web/tsconfig.app.json b/exporter/SynthesisFusionAddin/web/tsconfig.app.json new file mode 100644 index 0000000000..3627b90d96 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/exporter/SynthesisFusionAddin/web/tsconfig.json b/exporter/SynthesisFusionAddin/web/tsconfig.json new file mode 100644 index 0000000000..d32ff68200 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/tsconfig.json @@ -0,0 +1,4 @@ +{ + "files": [], + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] +} diff --git a/exporter/SynthesisFusionAddin/web/tsconfig.node.json b/exporter/SynthesisFusionAddin/web/tsconfig.node.json new file mode 100644 index 0000000000..3694a51714 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/exporter/SynthesisFusionAddin/web/vite.config.ts b/exporter/SynthesisFusionAddin/web/vite.config.ts new file mode 100644 index 0000000000..7115c16e12 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/vite.config.ts @@ -0,0 +1,23 @@ +import react from "@vitejs/plugin-react-swc" +import { defineConfig, loadEnv } from "vite" + +// https://vite.dev/config/ +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), "") + return { + plugins: [react()], + publicDir: false, + base: "./", + build: { + target: "esnext", + rollupOptions: { + output: { + manualChunks: { + mui: ["@mui/material", "@mui/icons-material"], + }, + }, + }, + sourcemap: env.NODE_ENV === "dev" ? "inline" : false, + }, + } +})