commit 4007862149c5065ea7dc5d9c747ae3fd7dadc648 Author: NilsUeter Date: Sun Apr 9 17:55:17 2023 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/index.html b/index.html new file mode 100644 index 0000000..bcbcbd6 --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + + + + + Vite + React + + + + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..3bf8f13 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "fancyscribe", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "jszip": "^3.10.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "@vitejs/plugin-react": "^3.1.0", + "vite": "^4.2.0" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..022860e --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,80 @@ +import JSZip from "jszip"; +import { Create40kRoster } from "./roster40k"; +import { useState } from "react"; +import { Roster } from "./Roster"; + +function App() { + const [roster, setRoster] = useState(); + function handleFileSelect(event) { + const files = event?.target?.files; + + if (files) { + const reader = new FileReader(); + reader.onerror = () => { + reader.abort(); + console.log("Failed to read roster file."); + }; + reader.onloadend = async () => { + const content = reader.result; + const xmldata = await unzip(content); + parseXML(xmldata); + }; + reader.readAsBinaryString(files[0]); + } + } + + const unzip = async (file) => { + if (file.charAt(0) !== "P") { + return file; + } else { + const jszip = new JSZip(); + const zip = await jszip.loadAsync(file); + return zip.file(/[^/]+\.ros/)[0].async("string"); // Get roster files that are in the root + } + }; + + function parseXML(xmldata) { + const parser = new DOMParser(); + const doc = parser.parseFromString(xmldata, "text/xml"); + if (!doc) return; + + // Determine roster type (game system). + const info = doc.querySelector("roster"); + if (!info) return; + + const gameType = info.getAttribute("gameSystemName"); + if (!gameType) return; + + const rosterName = info.getAttribute("name"); + if (rosterName) { + document.title = `PrettyScribe ${rosterName}`; + } + + if (gameType == "Warhammer 40,000 9th Edition") { + console.log(doc); + const roster = Create40kRoster(doc); + console.log(roster); + if (roster && roster._forces.length > 0) { + setRoster(roster); + /* const renderer = new Renderer40k(roster); + renderer.render(rosterTitle, rosterList, forceUnits); */ + } + } else { + console.log("No support for game type '" + gameType + "'."); + } + } + + return ( +
+ +
+ +
+
+ ); +} + +export default App; diff --git a/src/Roster.jsx b/src/Roster.jsx new file mode 100644 index 0000000..2531349 --- /dev/null +++ b/src/Roster.jsx @@ -0,0 +1,517 @@ +import factionBackground from "./assets/factionBackground.png"; +import adeptusAstartesIcon from "./assets/adeptusAstartesIcon.png"; +import meleeIcon from "./assets/meleeIcon.png"; +import rangedIcon from "./assets/rangedIcon.png"; +import { Arrow } from "./assets/icons"; + +export const Roster = ({ roster }) => { + if (!roster) { + return null; + } + const { _name, _forces } = roster; + return ( + <> +
+ {_name} +
+ {_forces.map((force, index) => ( + + ))} + + ); +}; + +const Force = ({ force }) => { + const { _units, rules } = force; + return ( + <> + {_units.map((unit, index) => ( + + ))} + + + ); +}; + +const Unit = ({ unit }) => { + const { _name, weapons, abilities, keywords, factions, rules, modelStats } = + unit; + + const meleeWeapons = weapons + .filter((weapon) => weapon._range === "Melee") + .sort((a, b) => a._selectionName.localeCompare(b._selectionName)); + const rangedWeapons = weapons + .filter((weapon) => weapon._range !== "Melee") + .sort((a, b) => a._selectionName.localeCompare(b._selectionName)); + return ( +
+
+
+
+ {_name} +
+
+ {modelStats.map((model, index) => ( + 1} + /> + ))} +
+
+
+
+
+ + + +
+
+ { + + + + + +
{Arrow} + Before selecting targets for this weapon, select one of its + profiles to make attacks with. +
+ } + +
+
+
+ ABILITIES +
+ + + + +
+
+
+ ); +}; + +const ModelStats = ({ modelStats, index, showName }) => { + let { move, toughness, save, wounds, leadership, _name } = modelStats; + if (!wounds) { + wounds = "/"; + } + return ( +
+ + + + + + {showName && ( +
+ {_name} +
+ )} +
+ ); +}; + +const FactionIcon = () => { + return ( +
+
+
+ ); +}; + +const Keywords = ({ keywords }) => { + return ( +
+ KEYWORDS: + + {[...keywords].join(", ")} + +
+ ); +}; + +const Factions = ({ factions }) => { + return ( +
+ + FACTION KEYWORDS: + + + {[...factions].join(", ")} + +
+ ); +}; + +const Rules = ({ rules }) => { + return ( +
+ RULES: + + {[...rules.keys()].map((rule) => rule).join(", ")} + +
+ ); +}; + +const Abilities = ({ abilities }) => { + return ( +
+ {[...abilities.keys()].map((ability) => ( +
+ {ability}:{" "} + {abilities.get(ability)} +
+ ))} +
+ ); +}; + +const Characteristic = ({ title, characteristic, index }) => { + return ( +
+ {index === 0 && ( +
{title}
+ )} + {characteristic} +
+ ); +}; + +const FancyBox = ({ children }) => { + return ( +
+
+
{children}
+
+
+ ); +}; + +const Weapons = ({ title, weapons, modelStats }) => { + const isMelee = title === "MELEE WEAPONS"; + return ( + <> + {weapons.length > 0 && ( + + + +
+ +
+ + {title} + RANGE + A + {isMelee ? "WS" : "BS"} + S + AP + D + + + )} + + {weapons.map((weapon, index) => ( + + ))} + {weapons.length > 0 && ( + + + + )} + + + ); +}; + +const Weapon = ({ weapon, modelStats, isMelee, index }) => { + let { _name, _selectionName, _range, _type, str, _ap, _damage } = weapon; + const [type, attacks] = _type.split(" "); + const bs = modelStats[0]._bs; + const ws = modelStats[0]._ws; + const strModel = modelStats[0].str; + const meleeAttacks = modelStats[0]._attacks; + + const differentProfiles = _selectionName !== _name; + const interestingType = type !== "Melee"; + if (differentProfiles && _name.endsWith(" grenades")) { + _name = _name.replace(" grenades", ""); + } + return ( + + + {differentProfiles && Arrow} + + +
+ {differentProfiles && _selectionName + " - "} + {_name} + {interestingType && ( + + [{type}] + + )} +
+ + {_range} + {isMelee ? meleeAttacks : attacks} + {isMelee ? ws : bs} + {isMelee ? calculateWeaponStrength(strModel, str) : str} + {_ap} + {_damage} + + ); +}; + +const calculateWeaponStrength = (strModel, strWeapon) => { + if (strWeapon.startsWith("x")) + return strModel * parseInt(strWeapon.replace("x", "")); + return strModel + parseInt(strWeapon, 10); +}; + +const ForceRules = ({ rules }) => { + return ( +
+ {[...rules.keys()] + .filter((rule) => !rule.startsWith("Explodes")) + .map((rule) => ( +
+ {rule}: {rules.get(rule)} +
+ ))} +
+ ); +}; diff --git a/src/assets/adeptusAstartesIcon.png b/src/assets/adeptusAstartesIcon.png new file mode 100644 index 0000000..f598e40 Binary files /dev/null and b/src/assets/adeptusAstartesIcon.png differ diff --git a/src/assets/factionBackground.png b/src/assets/factionBackground.png new file mode 100644 index 0000000..31bbd84 Binary files /dev/null and b/src/assets/factionBackground.png differ diff --git a/src/assets/icons.jsx b/src/assets/icons.jsx new file mode 100644 index 0000000..4894311 --- /dev/null +++ b/src/assets/icons.jsx @@ -0,0 +1,5 @@ +export const Arrow = ( + + + +); diff --git a/src/assets/keywordsBackground.png b/src/assets/keywordsBackground.png new file mode 100644 index 0000000..75c10cd Binary files /dev/null and b/src/assets/keywordsBackground.png differ diff --git a/src/assets/meleeIcon.png b/src/assets/meleeIcon.png new file mode 100644 index 0000000..ed5ea73 Binary files /dev/null and b/src/assets/meleeIcon.png differ diff --git a/src/assets/rangedIcon.png b/src/assets/rangedIcon.png new file mode 100644 index 0000000..71b7d98 Binary files /dev/null and b/src/assets/rangedIcon.png differ diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..4fa821a --- /dev/null +++ b/src/index.css @@ -0,0 +1,122 @@ +:root { + font-family: "Noto Sans", sans-serif, Inter, 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; + -webkit-text-size-adjust: 100%; +} + +* { + box-sizing: border-box; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +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; + } +} + +#root { + max-width: 1000px; + margin-left: auto; + margin-right: auto; +} + +.App { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 24px 32px; +} + +.weapons-table { + border-collapse: collapse; +} + +.weapons-table th { + font-size: 1.1em; + padding: 2px 8px; + height: 27px; + font-weight: 600; +} + +.weapons-table td { + padding: 1px 8px; + border-bottom: 1px dotted #9e9fa1; + text-align: center; +} + +.emptyRow { + height: 22.5px; +} + +.emptyRow td { + border-bottom: none; +} + +.rowOtherColor { + background-color: #d0d1d3; +} + +.differentProfiles + .differentProfiles { + border-top: 1px solid #dfe0e2; +} diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..5cc5991 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + , +) diff --git a/src/roster40k.js b/src/roster40k.js new file mode 100644 index 0000000..72945c1 --- /dev/null +++ b/src/roster40k.js @@ -0,0 +1,1161 @@ +export class BaseNotes { + _name = ""; + _customName = ""; + _customNotes = ""; + + name() { + if (this._customName) return this._customName; + return this._name; + } + + notes() { + return this._customNotes; + } + + equal(other) { + if (other == null) return false; + // Weapons in 40k have unique names + return this._name === other._name; + } +} + +/** A `selection` attached to a unit or model. */ +export class Upgrade extends BaseNotes { + _cost = new Costs(); + _count = 1; + + selectionName() { + return this.name(); + } + + toString() { + let string = this.selectionName(); + if (this._count > 1) string = `${this._count}x ${string}`; + if (this._cost.hasValues()) string += ` ${this._cost.toString()}`; + return string; + } +} + +/** A weapon `profile` that is under a `selection`. */ +export class Weapon extends Upgrade { + _selectionName = ""; + + _range = "Melee"; + _type = "Melee"; + str = "user"; + _ap = ""; + _damage = ""; + + abilities = ""; + + /** + * Name of this weapon's `selection`. This is different from name() because + * name() is used for sorting and deduping weapon profiles. + */ + selectionName() { + return this._selectionName || this.name(); + } +} + +export class WoundTracker extends BaseNotes { + _name = ""; + _table = new Map(); +} + +export class Explosion extends BaseNotes { + _name = ""; + _diceRoll = ""; + _distance = ""; + _mortalWounds = ""; +} + +export class Psyker extends BaseNotes { + _cast = ""; + _deny = ""; + _powers = ""; + _other = ""; +} + +export class PsychicPower extends BaseNotes { + _name = ""; + _manifest = 0; + _range = ""; + _details = ""; +} + +export const UnitRole = { + NONE: "NONE", + + // 40k + SCD: "SCD", + HQ: "HQ", + TR: "TR", + EL: "EL", + FA: "FA", + HS: "HS", + FL: "FL", + DT: "DT", + FT: "FT", + LW: "LW", + AGENTS: "AGENTS", + NF: "NF", +}; + +export const UnitRoleToString = [ + "None", + + // 40k + "Supreme Command Detachment", + "HQ", + "Troops", + "Elites", + "Fast Attack", + "Heavy Support", + "Flyer", + "Dedicated Transport", + "Fortification", + "Lord of War", + "Agent of the Imperium", + "No Force Org Slot", +]; + +export class Model extends BaseNotes { + _count = 0; + + // Characteristics + move = '0"'; + _ws = ""; + _bs = ""; + str = 4; + toughness = 4; + wounds = 1; + _attacks = ""; + leadership = 7; + save = ""; + + weapons = []; + _upgrades = []; + // TODO model upgrades (i.e. tau support systems) + _psyker = null; + _psychicPowers = []; + _explosions = []; + + equal(model) { + if (model == null) return false; + + if ( + this._name === model._name && + this._count === model._count && + this.weapons.length === model.weapons.length && + this._upgrades.length === model._upgrades.length + ) { + for (let wi = 0; wi < this.weapons.length; wi++) { + if (!this.weapons[wi].equal(model.weapons[wi])) { + return false; + } + } + for (let wi = 0; wi < this._upgrades.length; wi++) { + if (!this._upgrades[wi].equal(model._upgrades[wi])) { + return false; + } + } + + // TODO: check for the same psychic powers + if (this._psyker != null || model._psyker != null) return false; + + return true; + } + return false; + } + + nameAndGear() { + let name = super.name(); + + if (this.weapons.length > 0 || this._upgrades.length > 0) { + const gear = this.getDedupedWeaponsAndUpgrades(); + name += ` (${gear.map((u) => u.toString()).join(", ")})`; + } + return name; + } + + getDedupedWeaponsAndUpgrades() { + const deduped = []; + for (const upgrade of [...this.weapons, ...this._upgrades]) { + if (!deduped.some((e) => upgrade.selectionName() === e.selectionName())) { + deduped.push(upgrade); + } + } + return deduped; + } + + normalize() { + this.weapons.sort(CompareWeapon); + this._upgrades.sort(CompareObj); + + this.normalizeUpgrades(this.weapons); + this.normalizeUpgrades(this._upgrades); + } + + normalizeUpgrades(upgrades) { + for (let i = 0; i < upgrades.length - 1; i++) { + const upgrade = upgrades[i]; + if (upgrade._name === upgrades[i + 1]._name) { + upgrade._count += upgrades[i + 1]._count; + upgrade._cost.add(upgrades[i + 1]._cost); + upgrades.splice(i + 1, 1); + i--; + } + } + for (let upgrade of upgrades) { + if (upgrade._count % this._count == 0) { + upgrade._count /= this._count; + upgrade._cost._points /= this._count; + } + } + } +} + +export class Unit extends BaseNotes { + _roleRole = UnitRole.NONE; + factions = new Set(); + keywords = new Set(); + + abilities = {}; + rules = new Map(); + + _models = []; + modelStats = []; + _modelList = []; + weapons = []; + _spells = []; + _psykers = []; + _explosions = []; + + _cost = new Costs(); + + _woundTracker = []; + + nameWithExtraCosts() { + const extraCosts = []; // Track extra costs like cabal points. + for (const freeformCostType in this._cost._freeformValues) { + if (this._cost._freeformValues[freeformCostType] === 0) continue; + extraCosts.push( + `${this._cost._freeformValues[freeformCostType]}${freeformCostType}` + ); + } + return extraCosts.length + ? `${this.name()} [${extraCosts.join(", ")}]` + : this.name(); + } + + equal(unit) { + if (unit == null) return false; + + if ( + unit._name === this._name && + unit._role === this._role && + unit._models.length === this._models.length && + unit.modelStats.length === this.modelStats.length + ) { + for (let mi = 0; mi < this._models.length; mi++) { + if (!this._models[mi].equal(unit._models[mi])) { + return false; + } + } + + for (let mi = 0; mi < this.modelStats.length; mi++) { + if (!this.modelStats[mi].equal(unit.modelStats[mi])) { + return false; + } + } + + // Check how to replace without lodash + /* if (!_.isEqual(this.abilities, unit.abilities)) { + return false; + } else if (!_.isEqual(this.rules, unit.rules)) { + return false; + } */ + + return true; + } + return false; + } + + normalize() { + // Sort force units by role and name + this._models.sort(CompareModel); + this.modelStats.sort(CompareObj); + + for (let model of this._models) { + model.normalize(); + } + + for (let i = 0; i < this._models.length - 1; i++) { + const model = this._models[i]; + + if (model.nameAndGear() === this._models[i + 1].nameAndGear()) { + model._count++; + this._models.splice(i + 1, 1); + i--; + } + } + + for (let i = 0; i < this.modelStats.length - 1; i++) { + const model = this.modelStats[i]; + + if (model.equal(this.modelStats[i + 1])) { + this.modelStats.splice(i + 1, 1); + i--; + } + } + + this._modelList = this._models.map( + (model) => + (model._count > 1 ? `${model._count}x ` : "") + model.nameAndGear() + ); + this.weapons = this._models + .map((m) => m.weapons) + .reduce((acc, val) => acc.concat(val), []) + .sort(CompareWeapon) + .filter((weap, i, array) => weap.name() !== array[i - 1]?.name()); + + this._spells.push( + ...this._models + .map((m) => m._psychicPowers) + .reduce((acc, val) => acc.concat(val), []) + ); + this._psykers.push(...this._models.map((m) => m._psyker).filter((p) => p)); + this._explosions.push( + ...this._models + .map((m) => m._explosions) + .reduce((acc, val) => acc.concat(val), []) + ); + } +} + +export class Force extends BaseNotes { + _catalog = ""; + _faction = "Unknown"; + _factionRules = new Map(); + _configurations = []; + rules = new Map(); + _units = []; +} + +export class Roster40k extends BaseNotes { + _cost = new Costs(); + _forces = []; +} + +export class Costs { + _powerLevel = 0; + _commandPoints = 0; + _points = 0; + _freeformValues; + + hasValues() { + return ( + this._powerLevel !== 0 || this._commandPoints !== 0 || this._points !== 0 + ); + } + + toString() { + const values = []; + if (this._points !== 0) values.push(`${this._points} pts`); + if (this._powerLevel !== 0) values.push(`${this._powerLevel} PL`); + if (this._commandPoints !== 0) values.push(`${this._commandPoints} CP`); + return `[${values.join(" / ")}]`; + } + + add(other) { + this._powerLevel += other._powerLevel; + this._commandPoints += other._commandPoints; + this._points += other._points; + for (const name in other._freeformValues) { + this.addFreeformValue(name, other._freeformValues[name]); + } + } + + addFreeformValue(name, value) { + if (!this._freeformValues) this._freeformValues = {}; + const oldValue = this._freeformValues[name] || 0; + this._freeformValues[name] = oldValue + value; + } +} + +export function Create40kRoster(doc) { + // Determine roster type (game system). + let info = doc.querySelector("roster"); + if (info) { + const roster = new Roster40k(); + + const name = info.getAttributeNode("name")?.nodeValue; + if (name) { + roster._name = name; + } else { + roster._name = "40k Army Roster"; + } + + ParseRosterPoints(doc, roster); + ParseForces(doc, roster); + + return roster; + } +} + +function ParseRosterPoints(doc, roster) { + let costs = doc.querySelectorAll("roster>costs>cost"); + for (let cost of costs) { + roster._cost.add(ParseCost(cost)); + } +} + +function ParseForces(doc, roster) { + let forcesRoot = doc.querySelectorAll("roster>forces>force"); + for (let root of forcesRoot) { + if (root.hasAttribute("name") && root.hasAttribute("catalogueName")) { + let f = new Force(); + + let which = root.getAttributeNode("name")?.nodeValue; + let value = root.getAttributeNode("catalogueName")?.nodeValue; + + if (which) { + f._name = which; + } + if (value) { + f._catalog = value; + } + + // TODO: Determine force faction and faction specific rules. + + // Only include the allegiance rules once. + if (!DuplicateForce(f, roster)) { + const rules = root.querySelectorAll("force>rules>rule"); + for (let rule of rules) { + ExtractRuleDescription(rule, f.rules); + } + } + + ParseSelections(root, f); + + roster._forces.push(f); + } + } +} + +function ParseSelections(root, force) { + let selections = root.querySelectorAll("force>selections>selection"); + + for (let selection of selections) { + // What kind of selection is this + let selectionName = selection.getAttributeNode("name")?.nodeValue; + if (!selectionName) continue; + + if (selectionName.includes("Detachment Command Cost")) { + // Ignore Detachment Command cost + } else if ( + selectionName === "Battle Size" || + selectionName === "Gametype" + ) { + ParseConfiguration(selection, force); + } else if (selection.querySelector('profile[typeName="Unit"]')) { + const unit = ParseUnit(selection); + force._units.push(unit); + for (const entry of unit.rules.entries()) { + force.rules.set(entry[0], entry[1]); + } + } else if (selection.getAttribute("type") === "upgrade") { + ExtractRuleFromSelection(selection, force.rules); + ParseConfiguration(selection, force); + const props = selection.querySelectorAll("selections>selection"); + for (let prop of props) { + // sub-faction + const name = prop.getAttribute("name"); + if (name && prop.getAttribute("type") === "upgrade") { + if (force._faction === "Unknown") { + // pick the first upgrade we see + force._faction = name; + } + ExtractRuleFromSelection(prop, force._factionRules); + } + } + } else { + console.log("** UNEXPECTED SELECTION **", selectionName, selection); + } + } + + for (const key of force._factionRules.keys()) { + force.rules.delete(key); + } + + // Sort force units by role and name + force._units.sort((a, b) => { + if (a._role > b._role) return 1; + else if (a._role == b._role) { + if (a._name > b._name) return 1; + else if (a._name == b._name) return 0; + return -1; + } + return -1; + }); +} + +function ParseConfiguration(selection, force) { + const name = selection.getAttribute("name"); + if (!name) { + return; + } + const category = selection.querySelector("category")?.getAttribute("name"); + const subSelections = selection.querySelectorAll("selections>selection"); + const details = []; + let costs = GetSelectionCosts(selection); + for (const sel of subSelections) { + details.push(sel.getAttribute("name")); + costs.add(GetSelectionCosts(sel)); + } + + let configuration = + !category || category === "Configuration" ? name : `${category} - ${name}`; + if (details.length > 0) configuration += `: ${details.join(", ")}`; + if (costs.hasValues()) configuration += ` ${costs.toString()}`; + + force._configurations.push(configuration); +} + +function DuplicateForce(force, roster) { + if (!roster || !force) return false; + + for (let f of roster._forces) { + if (f._catalog === force._catalog) return true; + } + return false; +} + +function ExtractRuleFromSelection(root, map) { + const profiles = root.querySelectorAll("profiles>profile"); + for (const profile of profiles) { + // detachment rules + const profileName = profile.getAttribute("name"); + if (!profileName) continue; + + const profileType = profile.getAttribute("typeName"); + if ( + profileType === "Abilities" || + profileType === "Dynastic Code" || + profileType === "Household Tradition" + ) { + ParseProfileCharacteristics(profile, profileName, profileType, map); + } + } + + const rules = root.querySelectorAll("rules>rule"); + for (const rule of rules) { + ExtractRuleDescription(rule, map); + } +} + +function ExtractRuleDescription(rule, map) { + const ruleName = rule.getAttribute("name"); + const desc = rule.querySelector("description"); + if (ruleName && desc?.textContent) { + map.set(ruleName, desc.textContent); + } +} + +function LookupRole(roleText) { + switch (roleText) { + case "HQ": + return UnitRole.HQ; + case "Troops": + return UnitRole.TR; + case "Elites": + return UnitRole.EL; + case "Fast Attack": + return UnitRole.FA; + case "Heavy Support": + return UnitRole.HS; + case "Flyer": + return UnitRole.FL; + case "Dedicated Transport": + return UnitRole.DT; + case "Fortification": + return UnitRole.FT; + case "Lord of War": + return UnitRole.LW; + case "Agent of the Imperium": + return UnitRole.AGENTS; + case "No Force Org Slot": + return UnitRole.NF; + case "Primarch | Daemon Primarch | Supreme Commander": + return UnitRole.SCD; + } + return UnitRole.NONE; +} + +function ExpandBaseNotes(root, obj) { + obj._name = root.getAttributeNode("name")?.nodeValue; + + let element = root; + if ( + root.tagName === "profile" && + root.parentElement && + root.parentElement.parentElement + ) { + element = root.parentElement.parentElement; + } + + obj._customName = element.getAttributeNode("customName")?.nodeValue; + let child = element.firstElementChild; + if (child && child.tagName === "customNotes") { + obj._customNotes = child.textContent; + } + return obj._name; +} + +function ExtractNumberFromParent(root) { + // Get parent node (a selection) to determine model count. + if (root.parentElement && root.parentElement.parentElement) { + const parentSelection = root.parentElement.parentElement; + const countValue = parentSelection.getAttributeNode("number")?.nodeValue; + if (countValue) { + return +countValue; + } + } + + return 0; +} + +function GetImmediateSelections(root) { + // querySelectorAll(':scope > tagname') doesn't work with jsdom, so we hack + // around it: https://github.com/jsdom/jsdom/issues/2998 + const selections = []; + for (const child of root.children) { + if (child.tagName === "selections") { + for (const subChild of child.children) { + if (subChild.tagName === "selection") { + selections.push(subChild); + } + } + } + } + return selections; +} + +function HasImmediateProfileWithTypeName(root, typeName) { + // querySelectorAll(':scope > tagname') doesn't work with jsdom, so we hack + // around it: https://github.com/jsdom/jsdom/issues/2998 + for (const child of root.children) { + if (child.tagName === "profiles") { + for (const subChild of child.children) { + if ( + subChild.tagName === "profile" && + subChild.getAttribute("typeName") === typeName + ) { + return true; + } + } + } + } + return false; +} + +function GetSelectionCosts(selection) { + // querySelectorAll(':scope > tagname') doesn't work with jsdom, so we hack + // around it: https://github.com/jsdom/jsdom/issues/2998 + + const costs = new Costs(); + for (const child of selection.children) { + if (child.tagName === "costs") { + for (const subChild of child.children) { + costs.add(ParseCost(subChild)); + } + } + } + return costs; +} + +function ParseCost(cost) { + const costs = new Costs(); + const which = cost.getAttribute("name"); + const value = cost.getAttribute("value"); + if (which && value) { + if (which === " PL") { + costs._powerLevel += +value; + } else if (which === "pts") { + costs._points += +value; + } else if (which === "CP") { + costs._commandPoints += +value; + } else { + costs.addFreeformValue(which, +value); + } + } + return costs; +} + +function ParseUnit(root) { + let unit = new Unit(); + const unitName = ExpandBaseNotes(root, unit); + + let categories = root.querySelectorAll("categories>category"); + for (let cat of categories) { + const catName = cat.getAttributeNode("name")?.nodeValue; + if (catName) { + const factPattern = "Faction: "; + const factIndex = catName.lastIndexOf(factPattern); + if (factIndex >= 0) { + const factKeyword = catName.slice(factIndex + factPattern.length); + unit.factions.add(factKeyword); + } else { + const roleText = catName.trim(); + let unitRole = LookupRole(roleText); + if (unitRole != UnitRole.NONE) { + unit._role = unitRole; + } else { + // Keyword + unit.keywords.add(catName); + } + } + } + } + + const seenProfiles = []; + + // First, find model stats. These have typeName=Unit. + const modelStatsProfiles = Array.from( + root.querySelectorAll('profile[typeName="Unit"],profile[typeName="Model"]') + ); + ParseModelStatsProfiles(modelStatsProfiles, unit, unitName); + seenProfiles.push(...modelStatsProfiles); + + // Next, look for selections with models. These usually have type="model", + // but may have type="upgrade" containing a profile of type="Unit". + const modelSelections = []; + if (root.getAttribute("type") === "model") { + modelSelections.push(root); // Single-model unit. + } else { + const immediateSelections = GetImmediateSelections(root); + for (const selection of immediateSelections) { + if ( + selection.getAttribute("type") === "model" || + HasImmediateProfileWithTypeName(selection, "Unit") + ) { + modelSelections.push(selection); + } + } + // Some units are under a root selection with type="upgrade". + if (modelSelections.length === 0) { + modelSelections.push( + ...Array.from(root.querySelectorAll('selection[type="model"]')) + ); + } + // Some single-model units have type="unit" or type="upgrade". + if ( + modelSelections.length === 0 && + HasImmediateProfileWithTypeName(root, "Unit") + ) { + modelSelections.push(root); + } + } + + // Now, parse the model -- profiles for stats, and selections for upgrades. + for (const modelSelection of modelSelections) { + const profiles = Array.from( + modelSelection.querySelectorAll("profiles>profile") + ); + const unseenProfiles = profiles.filter((e) => !seenProfiles.includes(e)); + seenProfiles.push(...unseenProfiles); + + const model = new Model(); + model._name = modelSelection.getAttribute("name") || "Unknown Model"; + model._count = Number(modelSelection.getAttribute("number") || 1); + unit._models.push(model); + + // Find stats for all profiles (weapons, powers, abilities, etc). + ParseModelProfiles(profiles, model, unit); + + // Find all upgrades on the model. This may include weapons that were + // parsed from profiles (above), so dedupe those in nameAndGear(). + for (const upgradeSelection of modelSelection.querySelectorAll( + 'selections>selection[type="upgrade"]' + )) { + // Ignore selections without abilities but with sub-selection upgrades, + // since those sub-selections will be picked up individually. + if ( + upgradeSelection.querySelector( + 'selections>selection[type="upgrade"]' + ) && + !HasImmediateProfileWithTypeName(upgradeSelection, "Abilities") + ) + continue; + + let upgradeName = upgradeSelection.getAttribute("name"); + if (upgradeName) { + const upgrade = new Upgrade(); + upgrade._name = upgradeName; + upgrade._cost = GetSelectionCosts(upgradeSelection); + upgrade._count = Number(upgradeSelection.getAttribute("number")); + model._upgrades.push(upgrade); + } + } + } + + // Finally, look for profiles that are not under models. They may apply to + // a) model loadouts, if it's selection with a Weapon (eg Immortals) + // b) unit loadout, if it's a selection with an Ability (eg Bomb Squigs) + // c) abilities for the unit, if it's not under a child selection + let profiles = Array.from(root.querySelectorAll("profiles>profile")); + let unseenProfiles = profiles.filter((e) => !seenProfiles.includes(e)); + seenProfiles.push(...unseenProfiles); + if (unseenProfiles.length > 0) { + const unitUpgradesModel = new Model(); + unitUpgradesModel._name = "Unit Upgrades"; + ParseModelProfiles(unseenProfiles, unitUpgradesModel, unit); + if (unitUpgradesModel.weapons.length > 0 && unit._models.length > 0) { + // Apply weapons at the unit level to all models in the unit. + for (const model of unit._models) { + model.weapons.push(...unitUpgradesModel.weapons); + } + unitUpgradesModel.weapons.length = 0; // Clear the array. + } + if (unitUpgradesModel._psychicPowers.length > 0) { + // Add spells to the unit's spell list. However, we'll still need + // to add spell upgrade selections to the upgrade list, below. + unit._spells.push(...unitUpgradesModel._psychicPowers); + unitUpgradesModel._psychicPowers.length = 0; + } + if (unitUpgradesModel._psyker) { + unit._psykers.push(unitUpgradesModel._psyker); + unitUpgradesModel._psyker = null; + } + if (unitUpgradesModel._explosions.length > 0) { + unit._explosions.push(...unitUpgradesModel._explosions); + unitUpgradesModel._explosions.length = 0; + } + + // Look for any unit-level upgrade selections that we didn't catch + // previously, and stuff them in the "Unit Upgrades" model. + for (const selection of GetImmediateSelections(root)) { + if (selection.getAttribute("type") !== "upgrade") continue; + // Ignore model selections (which were already processed). + if (modelSelections.includes(selection)) continue; + // Ignore unit-level weapon selections; these were handled above. + if (selection.querySelector('profiles>profile[typeName="Weapon"]')) + continue; + + let name = selection.getAttribute("name"); + if (!name) continue; + + const upgrade = new Upgrade(); + upgrade._name = name; + upgrade._cost = GetSelectionCosts(selection); + upgrade._count = Number(selection.getAttribute("number")); + unitUpgradesModel._upgrades.push(upgrade); + } + + if ( + unitUpgradesModel.weapons.length > 0 || + unitUpgradesModel._upgrades.length > 0 + ) { + unit._models.push(unitUpgradesModel); + } + } + + // Only match costs->costs associated with the unit and not its children (model and weapon) costs. + let costs = root.querySelectorAll("costs>cost"); + for (let cost of costs) { + unit._cost.add(ParseCost(cost)); + } + + let rules = root.querySelectorAll("rules > rule"); + for (let rule of rules) { + ExtractRuleDescription(rule, unit.rules); + } + + unit.normalize(); + return unit; +} + +function ParseModelStatsProfiles(profiles, unit, unitName) { + for (const profile of profiles) { + const profileName = profile.getAttribute("name"); + const profileType = profile.getAttribute("typeName"); + if (!profileName || !profileType) return; + + const model = new Model(); + model._name = profileName; + unit.modelStats.push(model); + + ExpandBaseNotes(profile, model); + + const chars = profile.querySelectorAll("characteristics>characteristic"); + for (const char of chars) { + const charName = char.getAttribute("name"); + if (!charName) continue; + + if (char.textContent) { + switch (charName) { + case "M": + model.move = char.textContent; + break; + case "WS": + model._ws = char.textContent; + break; + case "BS": + model._bs = char.textContent; + break; + case "S": + model.str = +char.textContent; + break; + case "T": + model.toughness = +char.textContent; + break; + case "W": + model.wounds = +char.textContent; + break; + case "A": + model._attacks = char.textContent; + break; + case "Ld": + model.leadership = +char.textContent; + break; + case "Save": + model.save = char.textContent; + break; + } + } + } + } +} + +function ParseModelProfiles(profiles, model, unit) { + for (const profile of profiles) { + const profileName = profile.getAttribute("name"); + const typeName = profile.getAttribute("typeName"); + if (!profileName || !typeName) continue; + + if ( + typeName === "Unit" || + typeName === "Model" || + profile.getAttribute("type") === "model" + ) { + // Do nothing; these were already handled. + } else if (typeName === "Weapon") { + const weapon = ParseWeaponProfile(profile); + model.weapons.push(weapon); + } else if ( + typeName.includes("Wound Track") || + typeName.includes("Stat Damage") || + typeName.includes(" Wounds") + ) { + const tracker = ParseWoundTrackerProfile(profile); + unit._woundTracker.push(tracker); + } else if (typeName == "Psychic Power") { + const power = ParsePsychicPowerProfile(profile); + model._psychicPowers.push(power); + } else if (typeName.includes("Explosion")) { + const explosion = ParseExplosionProfile(profile); + model._explosions.push(explosion); + } else if (typeName == "Psyker") { + model._psyker = ParsePsykerProfile(profile); + } else { + // Everything else, like Prayers and Warlord Traits. + if (!unit.abilities[typeName]) unit.abilities[typeName] = new Map(); + ParseProfileCharacteristics( + profile, + profileName, + typeName, + unit.abilities[typeName] + ); + } + } +} + +function ParseProfileCharacteristics(profile, profileName, typeName, map) { + const chars = profile.querySelectorAll("characteristics>characteristic"); + for (const char of chars) { + if (!char.textContent) continue; + + const charName = char.getAttribute("name"); + if (charName && chars.length > 1) { + // Profiles with multiple characteristics need to distinguish them by name. + map.set([profileName, charName.toString()].join(" - "), char.textContent); + } else { + // Profiles with a single characteristic can ignore the char name. + map.set(profileName, char.textContent); + } + } +} + +function ParseWeaponProfile(profile) { + const weapon = new Weapon(); + ExpandBaseNotes(profile, weapon); + weapon._count = ExtractNumberFromParent(profile); + + let chars = profile.querySelectorAll("characteristics>characteristic"); + for (let char of chars) { + let charName = char.getAttribute("name"); + if (charName) { + if (char.textContent) { + switch (charName) { + case "Range": + weapon._range = char.textContent; + break; + case "Type": + weapon._type = char.textContent; + break; + case "S": + weapon.str = char.textContent; + break; + case "AP": + weapon._ap = char.textContent; + break; + case "D": + weapon._damage = char.textContent; + break; + case "Abilities": + weapon.abilities = char.textContent; + break; + } + } + } + } + // Keep track of the weapon's parent selection for its name, unless the + // weapon is directly under the unit's profile. + const selection = profile.parentElement?.parentElement; + const selectionName = selection?.getAttribute("name"); + if (selection?.getAttribute("type") === "upgrade" && selectionName) { + weapon._selectionName = selectionName; + weapon._cost = GetSelectionCosts(selection); + } + return weapon; +} + +function ParseWoundTrackerProfile(profile) { + let tracker = new WoundTracker(); + ExpandBaseNotes(profile, tracker); + let chars = profile.querySelectorAll("characteristics>characteristic"); + for (let char of chars) { + const charName = char.getAttribute("name"); + if (charName) { + if (char.textContent) { + tracker._table.set(charName, char.textContent); + } else { + tracker._table.set(charName, "-"); + } + } + } + return tracker; +} + +function ParsePsychicPowerProfile(profile) { + const power = new PsychicPower(); + ExpandBaseNotes(profile, power); + + const chars = profile.querySelectorAll("characteristics>characteristic"); + for (let char of chars) { + const charName = char.getAttribute("name"); + if (charName && char.textContent) { + switch (charName) { + case "Range": + power._range = char.textContent; + break; + case "Warp Charge": + power._manifest = +char.textContent; + break; + case "Details": + power._details = char.textContent; + break; + } + } + } + return power; +} + +function ParseExplosionProfile(profile) { + const explosion = new Explosion(); + ExpandBaseNotes(profile, explosion); + + const chars = profile.querySelectorAll("characteristics>characteristic"); + for (const char of chars) { + const charName = char.getAttribute("name"); + if (charName && char.textContent) { + switch (charName) { + case "Dice Roll": + explosion._diceRoll = char.textContent; + break; + case "Distance": + explosion._distance = char.textContent; + break; + case "Mortal Wounds": + explosion._mortalWounds = char.textContent; + break; + } + } + } + return explosion; +} + +function ParsePsykerProfile(profile) { + const psyker = new Psyker(); + ExpandBaseNotes(profile, psyker); + + const chars = profile.querySelectorAll("characteristics>characteristic"); + for (const char of chars) { + const charName = char.getAttribute("name"); + if (charName && char.textContent) { + switch (charName) { + case "Cast": + psyker._cast = char.textContent; + break; + case "Deny": + psyker._deny = char.textContent; + break; + case "Powers Known": + psyker._powers = char.textContent; + break; + case "Other": + psyker._other = char.textContent; + break; + } + } + } + return psyker; +} + +function CompareObj(a, b) { + return Compare(a._name, b._name); +} + +function CompareModel(a, b) { + if (a._name === b._name) { + return Compare(a.nameAndGear(), b.nameAndGear()); + } else if (a._name === "Unit Upgrades") { + // "Unit Upgrades", a special model name, is always sorted last. + return 1; + } else if (b._name === "Unit Upgrades") { + // "Unit Upgrades", a special model name, is always sorted last. + return -1; + } else { + return Compare(a._name, b._name); + } +} + +export function CompareWeapon(a, b) { + const aType = a._type.startsWith("Grenade") + ? 2 + : a._type.startsWith("Melee") + ? 1 + : 0; + const bType = b._type.startsWith("Grenade") + ? 2 + : b._type.startsWith("Melee") + ? 1 + : 0; + return aType - bType || a.name().localeCompare(b.name()); +} + +export function Compare(a, b) { + if (a > b) return 1; + else if (a == b) return 0; + return -1; +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..495de98 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,787 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== + +"@babel/core@^7.20.12": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + dependencies: + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-imports@^7.18.6": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + +"@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + +"@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + +"@babel/plugin-transform-react-jsx-self@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz#ec98d4a9baafc5a1eb398da4cf94afbb40254a54" + integrity sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-react-jsx-source@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" + integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/template@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@esbuild/android-arm64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz#893ad71f3920ccb919e1757c387756a9bca2ef42" + integrity sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA== + +"@esbuild/android-arm@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.15.tgz#143e0d4e4c08c786ea410b9a7739779a9a1315d8" + integrity sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg== + +"@esbuild/android-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.15.tgz#d2d12a7676b2589864281b2274355200916540bc" + integrity sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ== + +"@esbuild/darwin-arm64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz#2e88e79f1d327a2a7d9d06397e5232eb0a473d61" + integrity sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA== + +"@esbuild/darwin-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz#9384e64c0be91388c57be6d3a5eaf1c32a99c91d" + integrity sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg== + +"@esbuild/freebsd-arm64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz#2ad5a35bc52ebd9ca6b845dbc59ba39647a93c1a" + integrity sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg== + +"@esbuild/freebsd-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz#b513a48446f96c75fda5bef470e64d342d4379cd" + integrity sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ== + +"@esbuild/linux-arm64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz#9697b168175bfd41fa9cc4a72dd0d48f24715f31" + integrity sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA== + +"@esbuild/linux-arm@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz#5b22062c54f48cd92fab9ffd993732a52db70cd3" + integrity sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw== + +"@esbuild/linux-ia32@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz#eb28a13f9b60b5189fcc9e98e1024f6b657ba54c" + integrity sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q== + +"@esbuild/linux-loong64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz#32454bdfe144cf74b77895a8ad21a15cb81cfbe5" + integrity sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ== + +"@esbuild/linux-mips64el@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz#af12bde0d775a318fad90eb13a0455229a63987c" + integrity sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ== + +"@esbuild/linux-ppc64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz#34c5ed145b2dfc493d3e652abac8bd3baa3865a5" + integrity sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg== + +"@esbuild/linux-riscv64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz#87bd515e837f2eb004b45f9e6a94dc5b93f22b92" + integrity sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA== + +"@esbuild/linux-s390x@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz#20bf7947197f199ddac2ec412029a414ceae3aa3" + integrity sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg== + +"@esbuild/linux-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz#31b93f9c94c195e852c20cd3d1914a68aa619124" + integrity sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg== + +"@esbuild/netbsd-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz#8da299b3ac6875836ca8cdc1925826498069ac65" + integrity sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA== + +"@esbuild/openbsd-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz#04a1ec3d4e919714dba68dcf09eeb1228ad0d20c" + integrity sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w== + +"@esbuild/sunos-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz#6694ebe4e16e5cd7dab6505ff7c28f9c1c695ce5" + integrity sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ== + +"@esbuild/win32-arm64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz#1f95b2564193c8d1fee8f8129a0609728171d500" + integrity sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q== + +"@esbuild/win32-ia32@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz#c362b88b3df21916ed7bcf75c6d09c6bf3ae354a" + integrity sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w== + +"@esbuild/win32-x64@0.17.15": + version "0.17.15" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz#c2e737f3a201ebff8e2ac2b8e9f246b397ad19b8" + integrity sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA== + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/react-dom@^18.0.11": + version "18.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.11.tgz#321351c1459bc9ca3d216aefc8a167beec334e33" + integrity sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^18.0.28": + version "18.0.33" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.33.tgz#a1575160cb4376787c2f5fe0312302f824baa61e" + integrity sha512-sHxzVxeanvQyQ1lr8NSHaj0kDzcNiGpILEVt69g9S31/7PfMvNCKLKcsHw4lYKjs3cGNJjXSP4mYzX43QlnjNA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.3" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" + integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== + +"@vitejs/plugin-react@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz#d1091f535eab8b83d6e74034d01e27d73c773240" + integrity sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g== + dependencies: + "@babel/core" "^7.20.12" + "@babel/plugin-transform-react-jsx-self" "^7.18.6" + "@babel/plugin-transform-react-jsx-source" "^7.19.6" + magic-string "^0.27.0" + react-refresh "^0.14.0" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +browserslist@^4.21.3: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + +caniuse-lite@^1.0.30001449: + version "1.0.30001474" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001474.tgz#13b6fe301a831fe666cce8ca4ef89352334133d5" + integrity sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q== + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +csstype@^3.0.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + +debug@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +electron-to-chromium@^1.4.284: + version "1.4.352" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.352.tgz#be96bd7c2f4b980deebc9338a49a67430a33ed73" + integrity sha512-ikFUEyu5/q+wJpMOxWxTaEVk2M1qKqTGKKyfJmod1CPZxKfYnxVS41/GCBQg21ItBpZybyN8sNpRqCUGm+Zc4Q== + +esbuild@^0.17.5: + version "0.17.15" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.15.tgz#209ebc87cb671ffb79574db93494b10ffaf43cbc" + integrity sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw== + optionalDependencies: + "@esbuild/android-arm" "0.17.15" + "@esbuild/android-arm64" "0.17.15" + "@esbuild/android-x64" "0.17.15" + "@esbuild/darwin-arm64" "0.17.15" + "@esbuild/darwin-x64" "0.17.15" + "@esbuild/freebsd-arm64" "0.17.15" + "@esbuild/freebsd-x64" "0.17.15" + "@esbuild/linux-arm" "0.17.15" + "@esbuild/linux-arm64" "0.17.15" + "@esbuild/linux-ia32" "0.17.15" + "@esbuild/linux-loong64" "0.17.15" + "@esbuild/linux-mips64el" "0.17.15" + "@esbuild/linux-ppc64" "0.17.15" + "@esbuild/linux-riscv64" "0.17.15" + "@esbuild/linux-s390x" "0.17.15" + "@esbuild/linux-x64" "0.17.15" + "@esbuild/netbsd-x64" "0.17.15" + "@esbuild/openbsd-x64" "0.17.15" + "@esbuild/sunos-x64" "0.17.15" + "@esbuild/win32-arm64" "0.17.15" + "@esbuild/win32-ia32" "0.17.15" + "@esbuild/win32-x64" "0.17.15" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nanoid@^3.3.4: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== + +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +postcss@^8.4.21: + version "8.4.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rollup@^3.18.0: + version "3.20.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.20.2.tgz#f798c600317f216de2e4ad9f4d9ab30a89b690ff" + integrity sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg== + optionalDependencies: + fsevents "~2.3.2" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +vite@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.2.1.tgz#6c2eb337b0dfd80a9ded5922163b94949d7fc254" + integrity sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg== + dependencies: + esbuild "^0.17.5" + postcss "^8.4.21" + resolve "^1.22.1" + rollup "^3.18.0" + optionalDependencies: + fsevents "~2.3.2" + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==