~starkingdoms/starkingdoms

f0286e5ee2e4f56e49f820809a9f246974d18111 — core 1 year, 11 months ago 1cd4439
rewrite the client in svelte (part 1)
M starkingdoms-client/.prettierrc => starkingdoms-client/.prettierrc +6 -1
@@ 1,1 1,6 @@
{}
{
  "plugins": ["prettier-plugin-svelte"],
  "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }],
  "htmlWhitespaceSensitivity": "ignore",
  "bracketSameLine": true
}

M starkingdoms-client/index.html => starkingdoms-client/index.html +5 -55
@@ 1,3 1,5 @@
<!-- DO NOT CHANGE THIS FILE! -->
<!-- UI is rendered by Svelte. The root of the component tree is src/pages/Home.svelte. -->
<!doctype html>
<html lang="en">
  <head>


@@ 5,62 7,10 @@
    <title>StarKingdoms.IO</title>
  </head>
  <body class="bg-grid">
    <div class="popup popup-center popup-max-width-300" id="server_selector">
      <h1>StarKingdoms</h1>
      <h2>Join Game</h2>

      <form id="join-fm" class="form">
        <label for="username" class="label">Username</label>
        <input class="textentry" id="username" required autocomplete="off" />

        <label class="label" for="select">Server</label>
        <select id="select" class="select" autocomplete="off">
          <option>Loading server list, please wait...</option>
        </select>

        <button id="launch-btn" class="btn">Launch!</button>

        <span id="server-danger" class="server-danger hidden">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 20 20"
            fill="currentColor"
            class="server-danger-icon"
          >
            <path
              fill-rule="evenodd"
              d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"
              clip-rule="evenodd"
            />
          </svg>
          Here be dragons! You have a <b>prerelease server</b> selected. Expect
          bugs, and save data on this server may be wiped at any time.
        </span>

        <span id="account-info" class="account-info"
          >You are not logged in. Save data will be stored in your browser
          cache. <a href="/login/">Log in</a> to save your data on the server
          and sync it between devices.</span
        >
      </form>
    <div id="mount">
      <!-- Rendered by Svelte -->
    </div>

    <span class="footer-left" id="footer-left">StarKingdoms Client</span>
    <span class="footer-right" id="footer-right"
      >Made with
      <svg
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 20 20"
        fill="currentColor"
        class="footer-icon"
      >
        <path
          d="M9.653 16.915l-.005-.003-.019-.01a20.759 20.759 0 01-1.162-.682 22.045 22.045 0 01-2.582-1.9C4.045 12.733 2 10.352 2 7.5a4.5 4.5 0 018-2.828A4.5 4.5 0 0118 7.5c0 2.852-2.044 5.233-3.885 6.82a22.049 22.049 0 01-3.744 2.582l-.019.01-.005.003h-.002a.739.739 0 01-.69.001l-.002-.001z"
        />
      </svg>
      by the StarKingdoms team</span
    >

    <script type="module" src="src/routes/menu/main.ts"></script>
    <script type="module" src="src/page_loaders/home.ts"></script>
  </body>
</html>

M starkingdoms-client/package.json => starkingdoms-client/package.json +8 -1
@@ 6,15 6,22 @@
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
    "preview": "vite preview",
    "check": "svelte-check --tsconfig ./tsconfig.json"
  },
  "devDependencies": {
    "@sveltejs/vite-plugin-svelte": "^3.0.1",
    "@tsconfig/svelte": "^5.0.2",
    "@types/debug": "^4.1.12",
    "@types/node": "^20.10.0",
    "autoprefixer": "^10.4.16",
    "postcss": "^8.4.31",
    "prettier": "^3.1.0",
    "prettier-plugin-svelte": "^3.1.2",
    "sass": "^1.69.5",
    "svelte": "^4.2.8",
    "svelte-check": "^3.6.2",
    "tslib": "^2.6.2",
    "typescript": "^5.2.2",
    "vite": "^5.0.0"
  },

M starkingdoms-client/play/index.html => starkingdoms-client/play/index.html +6 -106
@@ 1,120 1,20 @@
<!-- DO NOT CHANGE THIS FILE! -->
<!-- UI is rendered by Svelte. The root of the component tree is src/pages/Play.svelte. -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
    />
      content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>StarKingdoms.IO</title>
  </head>
  <body>
    <div class="game" id="gamewindow"></div>

    <div
      class="popup popup-wmin log-hidden log-container popup-max-width-300"
      id="packet_log"
    >
      <h1>Packet Log</h1>
      <table class="log">
        <thead>
          <tr class="log-wfull">
            <th class="log-wfull log-lalign log-header log-w50">#</th>
            <th class="log-wfull log-lalign log-header">Dir</th>
            <th class="log-wfull log-lalign log-header log-w240">Type</th>
            <th class="log-wfull log-lalign log-header">Delta</th>
          </tr>
        </thead>
        <tbody id="log_body"></tbody>
      </table>
      <hr />
      <h1>Packet Explorer</h1>
      <p id="explorer_selected" class="mono">Selected: --</p>
      <table class="mono json" id="explorer_json"></table>
    </div>

    <div class="popup chat-container" id="chat">
      <h1>Chat</h1>
      <div class="chat-minimax" id="chat-minimax">
        <svg
          width="16"
          height="2"
          viewBox="0 0 4.2333332 0.52916663"
          version="1.1"
          id="svg1"
          style="position: absolute; top: 14px; left: 7px"
          xmlns="http://www.w3.org/2000/svg"
          xmlns:svg="http://www.w3.org/2000/svg"
        >
          <defs id="defs1" />
          <g id="layer1" transform="translate(0,-1.8520833)">
            <rect
              style="fill: #f9f9f9; stroke-width: 0"
              id="rect1"
              width="4.2333331"
              height="0.52916664"
              x="0"
              y="1.8520833"
            />
          </g>
        </svg>
      </div>
      <div id="chatbox" class="chat-table mono">
        <!-- Filled by script -->
      </div>
      <input
        placeholder="Enter message or command here..."
        class="chat-box"
        id="chatentry"
        required
        autocomplete="off"
      />
    <div id="mount">
      <!-- Rendered by Svelte -->
    </div>

    <div class="hud" id="hud">
      <div class="popup" id="hud-content-wrapper">
        <table>
          <thead>
            <tr>
              <th class="hud-d">Position</th>
              <th class="hud-d">Velocity</th>
              <th class="hud-d">Track Angle</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td id="pos">
                <span id="pos-val-x">--</span>, <span id="pos-val-y">--</span>
              </td>
              <td id="velocity">
                <span id="velocity-val">--</span>
              </td>
              <td id="track">
                <span id="track-val">--</span>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>

    <span class="footer-left" id="footer-left">StarKingdoms Client</span>
    <span class="footer-right" id="footer-right"
      >Made with
      <svg
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 20 20"
        fill="currentColor"
        class="footer-icon"
      >
        <path
          d="M9.653 16.915l-.005-.003-.019-.01a20.759 20.759 0 01-1.162-.682 22.045 22.045 0 01-2.582-1.9C4.045 12.733 2 10.352 2 7.5a4.5 4.5 0 018-2.828A4.5 4.5 0 0118 7.5c0 2.852-2.044 5.233-3.885 6.82a22.049 22.049 0 01-3.744 2.582l-.019.01-.005.003h-.002a.739.739 0 01-.69.001l-.002-.001z"
        />
      </svg>
      by the StarKingdoms team</span
    >

    <script type="module" src="../src/routes/ingame/main.ts"></script>
    <script type="module" src="../src/page_loaders/play.ts"></script>
  </body>
</html>

R starkingdoms-client/src/chat.ts => starkingdoms-client/src/components/Chatbox.svelte +81 -43
@@ 1,49 1,87 @@
export function addMessage(classname: string, message: string) {
  let chatbox = document.getElementById("chatbox");
  let should_scroll =
    chatbox!.scrollTop == chatbox!.scrollHeight - chatbox!.offsetHeight;
  let p = document.createElement("p");
  p.innerText = message;
  p.classList.add("message");
  p.classList.add(classname);
  if (chatbox.children.length > 128) {
    chatbox.children[0]!.remove();
  }
  chatbox!.appendChild(p);
  if (should_scroll) {
    chatbox!.scrollTop = chatbox!.scrollHeight;
<script lang="ts">
  export function addMessage(classname: string, message: string) {
    let chatbox = document.getElementById("chatbox")!;
    let should_scroll =
      chatbox!.scrollTop == chatbox!.scrollHeight - chatbox!.offsetHeight;
    let p = document.createElement("p");
    p.innerText = message;
    p.classList.add("message");
    p.classList.add(classname);
    if (chatbox.children.length > 128) {
      chatbox.children[0]!.remove();
    }
    chatbox!.appendChild(p);
    if (should_scroll) {
      chatbox!.scrollTop = chatbox!.scrollHeight;
    }
  }
}

/* I'm sorry for this code lmao */
export function chatViewToggle() {
  let el = document.getElementById("chat-minimax");
  let chat_modal = document.getElementById("chat");
  console.log("clickl");
  let to_hide = [
    document.getElementById("chatbox"),
    document.getElementById("chatentry"),
  ];
  if (el.hasAttribute("minimised")) {
    chat_modal.style.width = "";
    chat_modal.style.paddingRight = "";
    for (let element of to_hide) {
      element.style.display = "";
  /* I'm sorry for this code lmao */
  export function chatViewToggle() {
    let el = document.getElementById("chat-minimax")!;
    let chat_modal = document.getElementById("chat")!;
    console.log("clickl");
    let to_hide = [
      document.getElementById("chatbox")!,
      document.getElementById("chatentry")!,
    ];
    if (el.hasAttribute("minimised")) {
      chat_modal.style.width = "";
      chat_modal.style.paddingRight = "";
      for (let element of to_hide) {
        element.style.display = "";
      }
    } else {
      chat_modal.style.width = "unset";
      chat_modal.style.paddingRight =
        "4em"; /* magical number. don't touch >:( */
      for (let element of to_hide) {
        element.style.display = "none";
      }
    }
  } else {
    chat_modal.style.width = "unset";
    chat_modal.style.paddingRight = "4em"; /* magical number. don't touch >:( */
    for (let element of to_hide) {
      element.style.display = "none";

    /* inject that live svg hell yeah */
    if (el.hasAttribute("minimised")) {
      el.removeAttribute("minimised");
      el.innerHTML = `<svg height=16 style=position:absolute;top:7px;left:7px version=1.1 viewBox="0 0 4.2333332 0.52916663" width=16 xmlns=http://www.w3.org/2000/svg xmlns:svg=http://www.w3.org/2000/svg><defs id=defs1></defs><g id=layer1 transform=translate(0,-1.8520833)><rect height=0.52916664 id=rect1 style=fill:#f9f9f9;stroke-width:0 width=4.2333331 x=0 y=1.8520833></rect></g></svg>`;
    } else {
      el.setAttribute("minimised", "");
      el.innerHTML = `<svg height=16 width=16 style=position:absolute;top:7px;left:7px version=1.1 viewBox="0 0 4.2333332 4.233333" xmlns=http://www.w3.org/2000/svg xmlns:svg=http://www.w3.org/2000/svg><defs id=defs1 /><g id=layer1><rect height=0.52916664 id=rect1 style=fill:#f9f9f9;stroke-width:0 width=4.2333331 x=0 y=1.8520833 /><rect height=0.52916664 id=rect1-5 style=fill:#f9f9f9;stroke-width:0 width=4.2333331 x=-4.2333331 y=1.8520833 transform=rotate(-90) /></g></svg>`;
    }
  }
</script>

  /* inject that live svg hell yeah */
  if (el.hasAttribute("minimised")) {
    el.removeAttribute("minimised");
    el.innerHTML = `<svg height=16 style=position:absolute;top:7px;left:7px version=1.1 viewBox="0 0 4.2333332 0.52916663"width=16 xmlns=http://www.w3.org/2000/svg xmlns:svg=http://www.w3.org/2000/svg><defs id=defs1></defs><g id=layer1 transform=translate(0,-1.8520833)><rect height=0.52916664 id=rect1 style=fill:#f9f9f9;stroke-width:0 width=4.2333331 x=0 y=1.8520833></rect></g></svg>`;
  } else {
    el.setAttribute("minimised", "");
    el.innerHTML = `<svg height=16 width=16 style=position:absolute;top:7px;left:7px version=1.1 viewBox="0 0 4.2333332 4.233333"width=16 xmlns=http://www.w3.org/2000/svg xmlns:svg=http://www.w3.org/2000/svg><defs id=defs1 /><g id=layer1><rect height=0.52916664 id=rect1 style=fill:#f9f9f9;stroke-width:0 width=4.2333331 x=0 y=1.8520833 /><rect height=0.52916664 id=rect1-5 style=fill:#f9f9f9;stroke-width:0 width=4.2333331 x=-4.2333331 y=1.8520833 transform=rotate(-90) /></g></svg>`;
  }
}
<div class="popup chat-container" id="chat">
  <h1>Chat</h1>
  <div class="chat-minimax" id="chat-minimax">
    <svg
      width="16"
      height="2"
      viewBox="0 0 4.2333332 0.52916663"
      version="1.1"
      id="svg1"
      style="position: absolute; top: 14px; left: 7px"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:svg="http://www.w3.org/2000/svg">
      <defs id="defs1" />
      <g id="layer1" transform="translate(0,-1.8520833)">
        <rect
          style="fill: #f9f9f9; stroke-width: 0"
          id="rect1"
          width="4.2333331"
          height="0.52916664"
          x="0"
          y="1.8520833" />
      </g>
    </svg>
  </div>
  <div id="chatbox" class="chat-table mono">
    <!-- Filled by script -->
  </div>
  <input
    placeholder="Enter message or command here..."
    class="chat-box"
    id="chatentry"
    required
    autocomplete="off" />
</div>

M starkingdoms-client/src/config.ts => starkingdoms-client/src/config.ts +2 -0
@@ 46,3 46,5 @@ export async function loadConfig(): Promise<Config> {
    return CONFIG;
  }
}

export const DEFAULT_CONFIG = CONFIG;

R starkingdoms-client/src/routes/ingame/main.ts => starkingdoms-client/src/globals.ts +2 -61
@@ 1,11 1,6 @@
import createDebug from "debug";
import { ClientHub, hub_connect } from "../../hub.ts";
import "../../css/style.scss";
import "../../css/themes/catppuccin-mocha.scss";
import { Part, Planet } from "../../protocol.ts";
import { ClientHub } from "./hub.ts";
import { Part, Planet } from "./protocol.ts";
import * as PIXI from "pixi.js";
import { addMessage, chatViewToggle } from "../../chat.ts";
import { loadConfig } from "../../config.ts";

export interface GlobalData {
  client: ClientHub | null;


@@ 52,60 47,6 @@ export const global: GlobalData = {
  rendering: null,
};

async function init() {
  (window as any).__initialized = true;
  // @ts-ignore
  init = undefined;

  const config = await loadConfig();

  const logger = createDebug("main");
  logger(
    `Hello, world! StarKingdoms ${APP_VERSION} (${COMMIT_HASH}) at your service!`,
  );
  logger("Current view: /play");

  document.getElementById("footer-left")!.innerHTML =
    `StarKingdoms Client ${APP_VERSION} (${COMMIT_HASH})`;
  document.getElementById("chat-minimax")!.onclick = chatViewToggle;

  addMessage("server-message", "Connecting to the game server...");

  let params = new URLSearchParams(window.location.search);

  if (!params.has("srv")) {
    addMessage(
      "server-error",
      "Server ID missing. Redirecting to main menu in 5 seconds.",
    );
    setTimeout(() => {
      window.location.href = "/";
    }, 5000);
    return;
  }
  if (!params.has("username")) {
    addMessage(
      "server-error",
      "Username missing. Redirecting to main menu in 5 seconds.",
    );
    setTimeout(() => {
      window.location.href = "/";
    }, 5000);
    return;
  }

  let server_id = params.get("srv")!;
  let username = params.get("username")!;

  let server = config.servers[server_id];

  await hub_connect(server.clientHubUrl, username);
}

if ((window as any).__initialized === undefined) {
  init();
}

export function player(): Part | undefined {
  if (global.me !== null) {
    return global.parts_map.get(global.me!.part_id);

M starkingdoms-client/src/hub.ts => starkingdoms-client/src/hub.ts +17 -17
@@ 1,20 1,19 @@
import createDebug from "debug";
import {
import type {
  MessagePacket,
  MessageType,
  Packet,
  PacketType,
  PartPositionsPacket,
  PlanetPositionsPacket,
  PlayerLeavePacket,
  PlayerListPacket,
  SpawnPlayerPacket,
} from "./protocol.ts";
import { MessageType, PacketType } from "./protocol.ts";
import { appendPacket } from "./packet_ui.ts";
import { global } from "./routes/ingame/main.ts";
import { global } from "./globals.ts";
import { startRender } from "./rendering.ts";
import { addMessage } from "./chat.ts";
import { ButtonType } from "./protocol";
import { ButtonType } from "./protocol.ts";
import type Chatbox from "./components/Chatbox.svelte";

const logger = createDebug("hub");



@@ 30,6 29,7 @@ export function sendPacket(client: ClientHub, packet: Packet) {
export async function hub_connect(
  url: string,
  username: string,
  chatbox: Chatbox,
): Promise<ClientHub | null> {
  logger("connecting to client hub at " + url);



@@ 108,7 108,7 @@ export async function hub_connect(
    };
    document.onmousedown = (e) => {
      if (global.me !== null) {
        let me_transform = global.parts_map.get(global.me?.part_id).transform;
        let me_transform = global.parts_map.get(global.me?.part_id)!.transform;
        let x = e.clientX - window.innerWidth / 2 + me_transform.x;
        let y = e.clientY - window.innerHeight / 2 + me_transform.y;



@@ 130,12 130,11 @@ export async function hub_connect(
          },
        };
        sendPacket(client, packet);
        console.log(packet);
      }
    };
    document.onmouseup = (e) => {
      if (global.me !== null) {
        let me_transform = global.parts_map.get(global.me?.part_id).transform;
        let me_transform = global.parts_map.get(global.me?.part_id)!.transform;
        let x = e.clientX - window.innerWidth / 2 + me_transform.x;
        let y = e.clientY - window.innerHeight / 2 + me_transform.y;



@@ 157,7 156,6 @@ export async function hub_connect(
          },
        };
        sendPacket(client, packet);
        console.log(packet);
      }
    };



@@ 169,7 167,7 @@ export async function hub_connect(
        if (value.startsWith(".msg")) {
          let args = value.split(" ");
          if (args.length < 3) {
            addMessage("server-error", "Command error");
            chatbox.addMessage("server-error", "Command error");

            (<HTMLInputElement>document.getElementById("chatentry")!).value =
              "";


@@ 186,7 184,7 @@ export async function hub_connect(
          };
          sendPacket(client, chat_packet);

          addMessage("direct-message", `you -> ${target}: ${message}`);
          chatbox.addMessage("direct-message", `you -> ${target}: ${message}`);
        } else {
          let chat_packet: Packet = {
            t: PacketType.SendMessage,


@@ 259,7 257,6 @@ export async function hub_connect(
            ).toString();
          }
        }
        console.log(global.parts_map.size);
      } else if (packet.t == PacketType.PlayerLeave) {
        let p = <PlayerLeavePacket>packet.c;
        let username = global.players_map.get(p.id)!;


@@ 273,18 270,21 @@ export async function hub_connect(
        );

        if (p.message_type == MessageType.Server) {
          addMessage("server-message", `[SERVER] ${p.content}`);
          chatbox.addMessage("server-message", `[SERVER] ${p.content}`);
        } else if (p.message_type == MessageType.Chat) {
          addMessage("global-message", `${p.actor}: ${p.content}`);
          chatbox.addMessage("global-message", `${p.actor}: ${p.content}`);
        } else if (p.message_type == MessageType.Direct) {
          // actor is who sent the message. destination is not included in this packet
          if (p.actor === global.me!.username) {
            // skip (shown above)
          } else {
            addMessage("direct-message", `${p.actor} -> you: ${p.content}`);
            chatbox.addMessage(
              "direct-message",
              `${p.actor} -> you: ${p.content}`,
            );
          }
        } else {
          addMessage("server-error", `${p.content}`);
          chatbox.addMessage("server-error", `${p.content}`);
        }
      } else {
        logger(`unrecognized packet type ${packet.t}`);

A starkingdoms-client/src/icons/HeartIcon.svelte => starkingdoms-client/src/icons/HeartIcon.svelte +13 -0
@@ 0,0 1,13 @@
<script lang="ts">
  let clazz = "";
  export { clazz as class };
</script>

<svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 20 20"
  fill="currentColor"
  class={clazz}>
  <path
    d="M9.653 16.915l-.005-.003-.019-.01a20.759 20.759 0 01-1.162-.682 22.045 22.045 0 01-2.582-1.9C4.045 12.733 2 10.352 2 7.5a4.5 4.5 0 018-2.828A4.5 4.5 0 0118 7.5c0 2.852-2.044 5.233-3.885 6.82a22.049 22.049 0 01-3.744 2.582l-.019.01-.005.003h-.002a.739.739 0 01-.69.001l-.002-.001z" />
</svg>

A starkingdoms-client/src/icons/WarningIcon.svelte => starkingdoms-client/src/icons/WarningIcon.svelte +15 -0
@@ 0,0 1,15 @@
<script lang="ts">
  let clazz = "";
  export { clazz as class };
</script>

<svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 20 20"
  fill="currentColor"
  class={clazz}>
  <path
    fill-rule="evenodd"
    d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"
    clip-rule="evenodd" />
</svg>

M starkingdoms-client/src/packet_ui.ts => starkingdoms-client/src/packet_ui.ts +1 -1
@@ 1,4 1,4 @@
import { Packet, type_direction } from "./protocol.ts";
import { type Packet, type_direction } from "./protocol.ts";
import createDebug from "debug";

const logger = createDebug("jsonview");

A starkingdoms-client/src/page_loaders/home.ts => starkingdoms-client/src/page_loaders/home.ts +10 -0
@@ 0,0 1,10 @@
// DO NOT CHANGE THIS FILE!
// It's only purpose is to initialize the Svelte component tree.
// Actual code is in src/pages/Home.svelte.
import Home from "../pages/Home.svelte";

const page = new Home({
  target: document.getElementById("mount")!,
});

export default page;

A starkingdoms-client/src/page_loaders/play.ts => starkingdoms-client/src/page_loaders/play.ts +10 -0
@@ 0,0 1,10 @@
// DO NOT CHANGE THIS FILE!
// It's only purpose is to initialize the Svelte component tree.
// Actual code is in src/pages/Play.svelte.
import Play from "../pages/Play.svelte";

const page = new Play({
  target: document.getElementById("mount")!,
});

export default page;

A starkingdoms-client/src/pages/Home.svelte => starkingdoms-client/src/pages/Home.svelte +112 -0
@@ 0,0 1,112 @@
<script lang="ts">
  import { DEFAULT_CONFIG, loadConfig } from "../config.ts";
  import createDebug from "debug";
  import "../css/themes/catppuccin-mocha.scss";
  import "../css/style.scss";
  import { parseJwt } from "../jwt.ts";
  import HeartIcon from "../icons/HeartIcon.svelte";
  import WarningIcon from "../icons/WarningIcon.svelte";

  let config = DEFAULT_CONFIG;
  // Top-level await. Sets the default config, and overwrites it when the new config is avail. Thanks reactivity!
  (async () => {
    config = await loadConfig();
  })();

  const logger = createDebug("main");
  logger(
    `Hello, world! StarKingdoms ${APP_VERSION} (${COMMIT_HASH}) at your service!`,
  );
  logger("Current view: <index>");

  let is_logged_in = false;
  let token_username = undefined;

  if (window.localStorage.getItem("stk-token") != null) {
    let token = window.localStorage.getItem("stk-token")!;
    let token_parsed: any = parseJwt(token);
    is_logged_in = true;
    token_username = token_parsed.username;
  }

  const is_development = window.localStorage.getItem("stk-mode") === "debug";

  let nonprod_warning;

  let server;
  let username;

  function playGame() {
    window.location.href = `/play/?srv=${server}&username=${username}`;
  }

  function updateNonprodWarning() {
    if (
      config.servers[server] !== undefined &&
      !config.servers[server].isProduction
    ) {
      nonprod_warning.classList.remove("hidden");
    } else {
      nonprod_warning.classList.add("hidden");
    }
  }
</script>

<div class="popup popup-center popup-max-width-300" id="server_selector">
  <h1>StarKingdoms</h1>
  <h2>Join Game</h2>

  <form id="join-fm" class="form" on:submit|preventDefault={playGame}>
    <label for="username" class="label">Username</label>
    <input
      bind:value={username}
      class="textentry"
      id="username"
      required
      autocomplete="off" />

    <label class="label" for="select">Server</label>
    <select
      bind:value={server}
      on:change={updateNonprodWarning}
      id="select"
      class="select"
      autocomplete="off">
      {#each Object.entries(config.servers) as [server_id, server]}
        {#if server.isDevelopment && !is_development}
          <!-- Ignore. Dev server and we arent dev -->
        {:else}
          <option value={server_id}>{server.name}</option>
        {/if}
      {/each}
    </select>

    <button id="launch-btn" class="btn">Launch!</button>

    <span bind:this={nonprod_warning} class="server-danger hidden">
      <WarningIcon class="server-danger-icon" />
      Here be dragons! You have a
      <b>prerelease server</b>
      selected. Expect bugs, and save data on this server may be wiped at any time.
    </span>

    <span id="account-info" class="account-info">
      {#if is_logged_in}
        Logged in as {token_username}! Saves will be stored on the server.
      {:else}
        You are not logged in. Save data will be stored in your browser cache. <a
          href="/login/">
          Log in
        </a>
        to save your data on the server and sync it between devices.
      {/if}
    </span>
  </form>
</div>

<span class="footer-left">
  StarKingdoms Client {APP_VERSION} ({COMMIT_HASH})
</span>
<span class="footer-right">
  Made with <HeartIcon class="footer-icon" /> by the StarKingdoms team
</span>

A starkingdoms-client/src/pages/Play.svelte => starkingdoms-client/src/pages/Play.svelte +128 -0
@@ 0,0 1,128 @@
<script lang="ts">
  import HeartIcon from "../icons/HeartIcon.svelte";

  import createDebug from "debug";
  import { hub_connect } from "../hub.ts";
  import "../css/style.scss";
  import "../css/themes/catppuccin-mocha.scss";
  import { DEFAULT_CONFIG, loadConfig } from "../config.ts";
  import Chatbox from "../components/Chatbox.svelte";
  import { onMount } from "svelte";

  let config = DEFAULT_CONFIG;

  let chatbox: Chatbox;

  const logger = createDebug("main");
  logger(
    `Hello, world! StarKingdoms ${APP_VERSION} (${COMMIT_HASH}) at your service!`,
  );
  logger("Current view: /play");

  onMount(async () => {
    config = await loadConfig();

    chatbox.addMessage("server-message", "Connecting to the game server...");

    let params = new URLSearchParams(window.location.search);

    if (!params.has("srv")) {
      chatbox.addMessage(
        "server-error",
        "Server ID missing. Redirecting to main menu in 5 seconds.",
      );
      setTimeout(() => {
        window.location.href = "/";
      }, 5000);
      return;
    }
    if (!params.has("username")) {
      chatbox.addMessage(
        "server-error",
        "Username missing. Redirecting to main menu in 5 seconds.",
      );
      setTimeout(() => {
        window.location.href = "/";
      }, 5000);
      return;
    }
    let server_id = params.get("srv")!;
    let username = params.get("username")!;

    if (!Object.keys(config.servers).includes(server_id)) {
      chatbox.addMessage(
        "server-error",
        "The selected server is currently unavailable. Redirecting to main menu in 5 seconds.",
      );
      setTimeout(() => {
        window.location.href = "/";
      }, 5000);
      return;
    }

    let server = config.servers[server_id];

    await hub_connect(server.clientHubUrl, username, chatbox);
  });
</script>

<div class="game" id="gamewindow"></div>

<div
  class="popup popup-wmin log-hidden log-container popup-max-width-300"
  id="packet_log">
  <h1>Packet Log</h1>
  <table class="log">
    <thead>
      <tr class="log-wfull">
        <th class="log-wfull log-lalign log-header log-w50">#</th>
        <th class="log-wfull log-lalign log-header">Dir</th>
        <th class="log-wfull log-lalign log-header log-w240">Type</th>
        <th class="log-wfull log-lalign log-header">Delta</th>
      </tr>
    </thead>
    <tbody id="log_body"></tbody>
  </table>
  <hr />
  <h1>Packet Explorer</h1>
  <p id="explorer_selected" class="mono">Selected: --</p>
  <table class="mono json" id="explorer_json"></table>
</div>

<Chatbox bind:this={chatbox} />

<div class="hud" id="hud">
  <div class="popup" id="hud-content-wrapper">
    <table>
      <thead>
        <tr>
          <th class="hud-d">Position</th>
          <th class="hud-d">Velocity</th>
          <th class="hud-d">Track Angle</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td id="pos">
            <span id="pos-val-x">--</span>
            ,
            <span id="pos-val-y">--</span>
          </td>
          <td id="velocity">
            <span id="velocity-val">--</span>
          </td>
          <td id="track">
            <span id="track-val">--</span>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

<span class="footer-left">
  StarKingdoms Client {APP_VERSION} ({COMMIT_HASH})
</span>
<span class="footer-right">
  Made with <HeartIcon class="footer-icon" /> by the StarKingdoms team
</span>

M starkingdoms-client/src/rendering.ts => starkingdoms-client/src/rendering.ts +1 -1
@@ 1,5 1,5 @@
import * as PIXI from "pixi.js";
import { global, player } from "./routes/ingame/main.ts";
import { global, player } from "./globals.ts";
import { part_texture_url, planet_texture_url } from "./textures.ts";

const PART_WIDTH = 50;

D starkingdoms-client/src/routes/menu/main.ts => starkingdoms-client/src/routes/menu/main.ts +0 -77
@@ 1,77 0,0 @@
import { ConfigServer, loadConfig } from "../../config.ts";
import createDebug from "debug";
import "../../css/themes/catppuccin-mocha.scss";
import "../../css/style.scss";
import { parseJwt } from "../../jwt.ts";

// HMR wrapper, to prevent doing reinit twice
let initialized: boolean | undefined;

async function init() {
  let config = await loadConfig();

  const logger = createDebug("main");
  logger(
    `Hello, world! StarKingdoms ${APP_VERSION} (${COMMIT_HASH}) at your service!`,
  );
  logger("Current view: <index>");

  initialized = true;

  const account_info = document.getElementById("account-info")!;

  if (window.localStorage.getItem("stk-token") != null) {
    let token = window.localStorage.getItem("stk-token")!;
    let token_parsed: any = parseJwt(token);
    account_info.innerText = `Logged in as ${token_parsed.username}! Saves will be stored on the server.`;
  }

  const is_development = window.localStorage.getItem("stk-mode") === "debug";

  document.getElementById("footer-left")!.innerHTML =
    `StarKingdoms Client ${APP_VERSION} (${COMMIT_HASH})`;

  // fill in the select box
  const select_box = <HTMLSelectElement>document.getElementById("select")!;
  const username_box = <HTMLInputElement>document.getElementById("username")!;
  const nonprod_warning = document.getElementById("server-danger")!;

  const form = <HTMLFormElement>document.getElementById("join-fm")!;
  form.onsubmit = (e) => {
    e.preventDefault();
    window.location.href = `/play/?srv=${select_box.value}&username=${username_box.value}`;
  };

  select_box.innerHTML = "";

  select_box.onchange = () => {
    if (
      config.servers[select_box.value] !== undefined &&
      !config.servers[select_box.value].isProduction
    ) {
      nonprod_warning.classList.remove("hidden");
    } else {
      nonprod_warning.classList.add("hidden");
    }
  };

  for (let server_id in config.servers) {
    let server: ConfigServer = config.servers[server_id];

    if (server.isDevelopment && !is_development) continue;

    let new_option = document.createElement("option");
    new_option.value = server_id;
    new_option.innerText = server.name;

    select_box.appendChild(new_option);

    if (server.isPrimary) {
      select_box.value = server_id;
    }
  }
}

if (initialized === undefined) {
  init();
}

A starkingdoms-client/src/vite-env.d.ts => starkingdoms-client/src/vite-env.d.ts +2 -0
@@ 0,0 1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

A starkingdoms-client/svelte.config.js => starkingdoms-client/svelte.config.js +7 -0
@@ 0,0 1,7 @@
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";

export default {
  // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
  // for more information about preprocessors
  preprocess: vitePreprocess(),
};

M starkingdoms-client/tsconfig.json => starkingdoms-client/tsconfig.json +5 -3
@@ 10,7 10,6 @@
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,

    /* Linting */


@@ 18,7 17,10 @@
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "types": ["vite/client"]
    "allowJs": true,
    "checkJs": true,
    "isolatedModules": true,
    "allowSyntheticDefaultImports": true
  },
  "include": ["src"]
  "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
}

M starkingdoms-client/vite.config.ts => starkingdoms-client/vite.config.ts +2 -1
@@ 3,6 3,7 @@ import { resolve } from "path";
import * as child from "child_process";
//@ts-ignore
import autoprefixer from "autoprefixer";
import { svelte } from "@sveltejs/vite-plugin-svelte";

const commitHash = child
  .execSync("git describe --no-match --always --abbrev=8 --dirty")


@@ 10,7 11,7 @@ const commitHash = child
  .trim();

export default defineConfig({
  plugins: [],
  plugins: [svelte()],
  define: {
    APP_VERSION: JSON.stringify(process.env.npm_package_version),
    COMMIT_HASH: JSON.stringify(commitHash),

M starkingdoms-client/yarn.lock => starkingdoms-client/yarn.lock +491 -5
@@ 2,6 2,14 @@
# yarn lockfile v1


"@ampproject/remapping@^2.2.1":
  version "2.2.1"
  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
  integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==
  dependencies:
    "@jridgewell/gen-mapping" "^0.3.0"
    "@jridgewell/trace-mapping" "^0.3.9"

"@esbuild/android-arm64@0.19.7":
  version "0.19.7"
  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.7.tgz#646156aea43e8e6723de6e94a4ac07c5aed41be1"


@@ 112,6 120,59 @@
  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.7.tgz#02c4446f802706098d8e6ee70cf2b7aba96ded0b"
  integrity sha512-gRaP2sk6hc98N734luX4VpF318l3w+ofrtTu9j5L8EQXF+FzQKV6alCOHMVoJJHvVK/mGbwBXfOL1HETQu9IGQ==

"@jridgewell/gen-mapping@^0.3.0":
  version "0.3.3"
  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
  integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
  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.1"
  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
  integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==

"@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.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15":
  version "1.4.15"
  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
  integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==

"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9":
  version "0.3.20"
  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f"
  integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
  dependencies:
    "@jridgewell/resolve-uri" "^3.1.0"
    "@jridgewell/sourcemap-codec" "^1.4.14"

"@nodelib/fs.scandir@2.1.5":
  version "2.1.5"
  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
  dependencies:
    "@nodelib/fs.stat" "2.0.5"
    run-parallel "^1.1.9"

"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
  version "2.0.5"
  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==

"@nodelib/fs.walk@^1.2.3":
  version "1.2.8"
  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
  dependencies:
    "@nodelib/fs.scandir" "2.1.5"
    fastq "^1.6.0"

"@pixi/accessibility@7.3.2":
  version "7.3.2"
  resolved "https://registry.yarnpkg.com/@pixi/accessibility/-/accessibility-7.3.2.tgz#e823d96a2d032a5bbaccdf547fcf46746bfb865a"


@@ 392,6 453,31 @@
  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.5.2.tgz#7e59216d929a6b444304000be40c32d2d127fe4f"
  integrity sha512-pL0RXRHuuGLhvs7ayX/SAHph1hrDPXOM5anyYUQXWJEENxw3nfHkzv8FfVlEVcLyKPAEgDRkd6RKZq2SMqS/yg==

"@sveltejs/vite-plugin-svelte-inspector@^2.0.0-next.0 || ^2.0.0":
  version "2.0.0"
  resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz#365afaa0dd63517838ce4686a3dc3982be348a9b"
  integrity sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==
  dependencies:
    debug "^4.3.4"

"@sveltejs/vite-plugin-svelte@^3.0.1":
  version "3.0.1"
  resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.1.tgz#475d3496a2a1c7fb4ec6ee3a4d3f0af01fb052e1"
  integrity sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==
  dependencies:
    "@sveltejs/vite-plugin-svelte-inspector" "^2.0.0-next.0 || ^2.0.0"
    debug "^4.3.4"
    deepmerge "^4.3.1"
    kleur "^4.1.5"
    magic-string "^0.30.5"
    svelte-hmr "^0.15.3"
    vitefu "^0.2.5"

"@tsconfig/svelte@^5.0.2":
  version "5.0.2"
  resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-5.0.2.tgz#c9ed3575c5445afb14965bb76e8446fbf7e4a0e6"
  integrity sha512-BRbo1fOtyVbhfLyuCWw6wAWp+U8UQle+ZXu84MYYWzYSEB28dyfnRBIE99eoG+qdAC0po6L2ScIEivcT07UaMA==

"@types/css-font-loading-module@^0.0.7":
  version "0.0.7"
  resolved "https://registry.yarnpkg.com/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz#2f98ede46acc0975de85c0b7b0ebe06041d24601"


@@ 409,6 495,11 @@
  resolved "https://registry.yarnpkg.com/@types/earcut/-/earcut-2.1.4.tgz#5811d7d333048f5a7573b22ddc84923e69596da6"
  integrity sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==

"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.1":
  version "1.0.5"
  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
  integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==

"@types/ms@*":
  version "0.7.34"
  resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"


@@ 426,6 517,16 @@
  resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz#90267db13f64d6e9ccb5ae3eac92786a7c77a516"
  integrity sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==

"@types/pug@^2.0.6":
  version "2.0.10"
  resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.10.tgz#52f8dbd6113517aef901db20b4f3fca543b88c1f"
  integrity sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==

acorn@^8.10.0, acorn@^8.9.0:
  version "8.11.3"
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
  integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==

anymatch@~3.1.2:
  version "3.1.3"
  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"


@@ 434,6 535,13 @@ anymatch@~3.1.2:
    normalize-path "^3.0.0"
    picomatch "^2.0.4"

aria-query@^5.3.0:
  version "5.3.0"
  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
  integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
  dependencies:
    dequal "^2.0.3"

autoprefixer@^10.4.16:
  version "10.4.16"
  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8"


@@ 446,12 554,32 @@ autoprefixer@^10.4.16:
    picocolors "^1.0.0"
    postcss-value-parser "^4.2.0"

axobject-query@^3.2.1:
  version "3.2.1"
  resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
  integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==
  dependencies:
    dequal "^2.0.3"

balanced-match@^1.0.0:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==

binary-extensions@^2.0.0:
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
  integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==

braces@~3.0.2:
brace-expansion@^1.1.7:
  version "1.1.11"
  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
  dependencies:
    balanced-match "^1.0.0"
    concat-map "0.0.1"

braces@^3.0.2, braces@~3.0.2:
  version "3.0.2"
  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==


@@ 468,6 596,11 @@ browserslist@^4.21.10:
    node-releases "^2.0.13"
    update-browserslist-db "^1.0.13"

buffer-crc32@^0.2.5:
  version "0.2.13"
  resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
  integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==

call-bind@^1.0.0:
  version "1.0.5"
  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"


@@ 477,12 610,17 @@ call-bind@^1.0.0:
    get-intrinsic "^1.2.1"
    set-function-length "^1.1.1"

callsites@^3.0.0:
  version "3.1.0"
  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==

caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541:
  version "1.0.30001565"
  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz#a528b253c8a2d95d2b415e11d8b9942acc100c4f"
  integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==

"chokidar@>=3.0.0 <4.0.0":
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1:
  version "3.5.3"
  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==


@@ 497,6 635,30 @@ caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541:
  optionalDependencies:
    fsevents "~2.3.2"

code-red@^1.0.3:
  version "1.0.4"
  resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35"
  integrity sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==
  dependencies:
    "@jridgewell/sourcemap-codec" "^1.4.15"
    "@types/estree" "^1.0.1"
    acorn "^8.10.0"
    estree-walker "^3.0.3"
    periscopic "^3.1.0"

concat-map@0.0.1:
  version "0.0.1"
  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==

css-tree@^2.3.1:
  version "2.3.1"
  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
  integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
  dependencies:
    mdn-data "2.0.30"
    source-map-js "^1.0.1"

debug@^4.3.4:
  version "4.3.4"
  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"


@@ 504,6 666,11 @@ debug@^4.3.4:
  dependencies:
    ms "2.1.2"

deepmerge@^4.3.1:
  version "4.3.1"
  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
  integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==

define-data-property@^1.1.1:
  version "1.1.1"
  resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"


@@ 513,6 680,16 @@ define-data-property@^1.1.1:
    gopd "^1.0.1"
    has-property-descriptors "^1.0.0"

dequal@^2.0.3:
  version "2.0.3"
  resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
  integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==

detect-indent@^6.1.0:
  version "6.1.0"
  resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
  integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==

earcut@^2.2.4:
  version "2.2.4"
  resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a"


@@ 523,6 700,11 @@ electron-to-chromium@^1.4.535:
  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.599.tgz#754d368c7d6086c92099236de10dbbc93dfb7688"
  integrity sha512-FdLI0/h+PvShEqmBMnZCdbgabAuQiiM9Ph8hVGmPOR5GU1XXZgwLRCMogE63OrUxcDEOBlEMVYAgtkJjWFnhRA==

es6-promise@^3.1.2:
  version "3.3.1"
  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
  integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==

esbuild@^0.19.3:
  version "0.19.7"
  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.7.tgz#b9a7235097b81278dcf090e2532ed13c95a2ee84"


@@ 556,11 738,36 @@ escalade@^3.1.1:
  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==

estree-walker@^3.0.0, estree-walker@^3.0.3:
  version "3.0.3"
  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
  integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
  dependencies:
    "@types/estree" "^1.0.0"

eventemitter3@^4.0.0:
  version "4.0.7"
  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
  integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==

fast-glob@^3.2.7:
  version "3.3.2"
  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
  integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
  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.4"

fastq@^1.6.0:
  version "1.16.0"
  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320"
  integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==
  dependencies:
    reusify "^1.0.4"

fill-range@^7.0.1:
  version "7.0.1"
  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"


@@ 573,6 780,11 @@ fraction.js@^4.3.6:
  resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
  integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==

fs.realpath@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==

fsevents@~2.3.2, fsevents@~2.3.3:
  version "2.3.3"
  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"


@@ 593,13 805,25 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@
    has-symbols "^1.0.3"
    hasown "^2.0.0"

glob-parent@~5.1.2:
glob-parent@^5.1.2, glob-parent@~5.1.2:
  version "5.1.2"
  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
  dependencies:
    is-glob "^4.0.1"

glob@^7.1.3:
  version "7.2.3"
  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
  dependencies:
    fs.realpath "^1.0.0"
    inflight "^1.0.4"
    inherits "2"
    minimatch "^3.1.1"
    once "^1.3.0"
    path-is-absolute "^1.0.0"

gopd@^1.0.1:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"


@@ 607,6 831,11 @@ gopd@^1.0.1:
  dependencies:
    get-intrinsic "^1.1.3"

graceful-fs@^4.1.3:
  version "4.2.11"
  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==

has-property-descriptors@^1.0.0:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340"


@@ 636,6 865,27 @@ immutable@^4.0.0:
  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f"
  integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==

import-fresh@^3.2.1:
  version "3.3.0"
  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
  dependencies:
    parent-module "^1.0.0"
    resolve-from "^4.0.0"

inflight@^1.0.4:
  version "1.0.6"
  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
  dependencies:
    once "^1.3.0"
    wrappy "1"

inherits@2:
  version "2.0.4"
  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==

is-binary-path@~2.1.0:
  version "2.1.0"
  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"


@@ 660,11 910,82 @@ is-number@^7.0.0:
  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==

is-reference@^3.0.0, is-reference@^3.0.1:
  version "3.0.2"
  resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.2.tgz#154747a01f45cd962404ee89d43837af2cba247c"
  integrity sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==
  dependencies:
    "@types/estree" "*"

ismobilejs@^1.1.0:
  version "1.1.1"
  resolved "https://registry.yarnpkg.com/ismobilejs/-/ismobilejs-1.1.1.tgz#c56ca0ae8e52b24ca0f22ba5ef3215a2ddbbaa0e"
  integrity sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==

kleur@^4.1.5:
  version "4.1.5"
  resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
  integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==

locate-character@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/locate-character/-/locate-character-3.0.0.tgz#0305c5b8744f61028ef5d01f444009e00779f974"
  integrity sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==

magic-string@^0.30.4, magic-string@^0.30.5:
  version "0.30.5"
  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9"
  integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==
  dependencies:
    "@jridgewell/sourcemap-codec" "^1.4.15"

mdn-data@2.0.30:
  version "2.0.30"
  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
  integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==

merge2@^1.3.0:
  version "1.4.1"
  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==

micromatch@^4.0.4:
  version "4.0.5"
  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
  dependencies:
    braces "^3.0.2"
    picomatch "^2.3.1"

min-indent@^1.0.0:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
  integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==

minimatch@^3.1.1:
  version "3.1.2"
  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
  dependencies:
    brace-expansion "^1.1.7"

minimist@^1.2.0, minimist@^1.2.6:
  version "1.2.8"
  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
  integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==

mkdirp@^0.5.1:
  version "0.5.6"
  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
  integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
  dependencies:
    minimist "^1.2.6"

mri@^1.1.0:
  version "1.2.0"
  resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
  integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==

ms@2.1.2:
  version "2.1.2"
  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"


@@ 695,12 1016,40 @@ object-inspect@^1.9.0:
  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
  integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==

once@^1.3.0:
  version "1.4.0"
  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
  dependencies:
    wrappy "1"

parent-module@^1.0.0:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
  dependencies:
    callsites "^3.0.0"

path-is-absolute@^1.0.0:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==

periscopic@^3.1.0:
  version "3.1.0"
  resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.1.0.tgz#7e9037bf51c5855bd33b48928828db4afa79d97a"
  integrity sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==
  dependencies:
    "@types/estree" "^1.0.0"
    estree-walker "^3.0.0"
    is-reference "^3.0.0"

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==

picomatch@^2.0.4, picomatch@^2.2.1:
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
  version "2.3.1"
  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==


@@ 755,6 1104,11 @@ postcss@^8.4.31:
    picocolors "^1.0.0"
    source-map-js "^1.0.2"

prettier-plugin-svelte@^3.1.2:
  version "3.1.2"
  resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-3.1.2.tgz#2e050eb56dbb467a42c45ad6ce18bb277d28ffa0"
  integrity sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==

prettier@^3.1.0:
  version "3.1.0"
  resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.0.tgz#c6d16474a5f764ea1a4a373c593b779697744d5e"


@@ 772,6 1126,11 @@ qs@^6.11.2:
  dependencies:
    side-channel "^1.0.4"

queue-microtask@^1.2.2:
  version "1.2.3"
  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==

readdirp@~3.6.0:
  version "3.6.0"
  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"


@@ 779,6 1138,23 @@ readdirp@~3.6.0:
  dependencies:
    picomatch "^2.2.1"

resolve-from@^4.0.0:
  version "4.0.0"
  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==

reusify@^1.0.4:
  version "1.0.4"
  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==

rimraf@^2.5.2:
  version "2.7.1"
  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
  dependencies:
    glob "^7.1.3"

rollup@^4.2.0:
  version "4.5.2"
  resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.5.2.tgz#2cf0ef0a57cb4038c50a66356684fd30071d0595"


@@ 798,6 1174,30 @@ rollup@^4.2.0:
    "@rollup/rollup-win32-x64-msvc" "4.5.2"
    fsevents "~2.3.2"

run-parallel@^1.1.9:
  version "1.2.0"
  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
  dependencies:
    queue-microtask "^1.2.2"

sade@^1.7.4:
  version "1.8.1"
  resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
  integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
  dependencies:
    mri "^1.1.0"

sander@^0.5.0:
  version "0.5.1"
  resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad"
  integrity sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==
  dependencies:
    es6-promise "^3.1.2"
    graceful-fs "^4.1.3"
    mkdirp "^0.5.1"
    rimraf "^2.5.2"

sass@^1.69.5:
  version "1.69.5"
  resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.5.tgz#23e18d1c757a35f2e52cc81871060b9ad653dfde"


@@ 826,11 1226,77 @@ side-channel@^1.0.4:
    get-intrinsic "^1.0.2"
    object-inspect "^1.9.0"

"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
sorcery@^0.11.0:
  version "0.11.0"
  resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.11.0.tgz#310c80ee993433854bb55bb9aa4003acd147fca8"
  integrity sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==
  dependencies:
    "@jridgewell/sourcemap-codec" "^1.4.14"
    buffer-crc32 "^0.2.5"
    minimist "^1.2.0"
    sander "^0.5.0"

"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, 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==

strip-indent@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
  integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
  dependencies:
    min-indent "^1.0.0"

svelte-check@^3.6.2:
  version "3.6.2"
  resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-3.6.2.tgz#a6922160e17e93c6f5fa2b18ec342cc4c70d60ab"
  integrity sha512-E6iFh4aUCGJLRz6QZXH3gcN/VFfkzwtruWSRmlKrLWQTiO6VzLsivR6q02WYLGNAGecV3EocqZuCDrC2uttZ0g==
  dependencies:
    "@jridgewell/trace-mapping" "^0.3.17"
    chokidar "^3.4.1"
    fast-glob "^3.2.7"
    import-fresh "^3.2.1"
    picocolors "^1.0.0"
    sade "^1.7.4"
    svelte-preprocess "^5.1.0"
    typescript "^5.0.3"

svelte-hmr@^0.15.3:
  version "0.15.3"
  resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.15.3.tgz#df54ccde9be3f091bf5f18fc4ef7b8eb6405fbe6"
  integrity sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==

svelte-preprocess@^5.1.0:
  version "5.1.3"
  resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz#7682239fe53f724c845b53026816fdfe15d028f9"
  integrity sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==
  dependencies:
    "@types/pug" "^2.0.6"
    detect-indent "^6.1.0"
    magic-string "^0.30.5"
    sorcery "^0.11.0"
    strip-indent "^3.0.0"

svelte@^4.2.8:
  version "4.2.8"
  resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.8.tgz#a279d8b6646131ffb11bc692840f8839b8ae4ed1"
  integrity sha512-hU6dh1MPl8gh6klQZwK/n73GiAHiR95IkFsesLPbMeEZi36ydaXL/ZAb4g9sayT0MXzpxyZjR28yderJHxcmYA==
  dependencies:
    "@ampproject/remapping" "^2.2.1"
    "@jridgewell/sourcemap-codec" "^1.4.15"
    "@jridgewell/trace-mapping" "^0.3.18"
    acorn "^8.9.0"
    aria-query "^5.3.0"
    axobject-query "^3.2.1"
    code-red "^1.0.3"
    css-tree "^2.3.1"
    estree-walker "^3.0.3"
    is-reference "^3.0.1"
    locate-character "^3.0.0"
    magic-string "^0.30.4"
    periscopic "^3.1.0"

to-regex-range@^5.0.1:
  version "5.0.1"
  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"


@@ 838,6 1304,16 @@ to-regex-range@^5.0.1:
  dependencies:
    is-number "^7.0.0"

tslib@^2.6.2:
  version "2.6.2"
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==

typescript@^5.0.3:
  version "5.3.3"
  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
  integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==

typescript@^5.2.2:
  version "5.3.2"
  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43"


@@ 874,3 1350,13 @@ vite@^5.0.0:
    rollup "^4.2.0"
  optionalDependencies:
    fsevents "~2.3.3"

vitefu@^0.2.5:
  version "0.2.5"
  resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969"
  integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==

wrappy@1:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==