mirror of
https://github.com/Krafpy/KSP-MGA-Planner.git
synced 2025-12-12 07:40:41 -08:00
534 lines
31 KiB
HTML
534 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8"/>
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||
<link rel="shortcut icon" href="#">
|
||
<link rel="stylesheet" type="text/css" href="style.css">
|
||
<!-- icons -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css">
|
||
<title>KSP MGA Planner</title>
|
||
</head>
|
||
<body>
|
||
<div id="container">
|
||
<header>
|
||
<a id="github-link" href="https://github.com/nmisyats/KSP-MGA-Planner">
|
||
<img src="logos/github-mark-white.svg" alt="Github link">
|
||
</a>
|
||
<h1>Multiple Gravity Assist Trajectory Planner for KSP</h1>
|
||
<h6>By Krafpy</h6>
|
||
<p>
|
||
Gravity assists are an efficient method to save fuel in interplanetary missions.
|
||
Instead of making a direct transfer from the departure planet to the destination one,
|
||
we can perform <em>fly-by</em>'s of intermediate planets to let their gravitational pull deviate
|
||
our inital trajectory, without the need to burn fuel.
|
||
</p>
|
||
<p>
|
||
This method is successfully used in real world missions, like the famous
|
||
<a href="https://en.wikipedia.org/wiki/Timeline_of_Cassini%E2%80%93Huygens">Cassini–Huygens</a> mission.
|
||
<p>
|
||
Gravity assist maneuvers can be achieved manually in KSP
|
||
(see <a href="https://www.youtube.com/watch?v=16jr7WWGSxo">Scott Manley's tutorial</a>).
|
||
However, unlike direct transfers, <em>multiple gravity assist</em> trajectories (MGA) are
|
||
<em>non trivial</em> to calculate.
|
||
</p>
|
||
<p>
|
||
This tool allows planning MGA trajectories with the details of each maneuver needed,
|
||
with consideration of orbits' inclinations and eccentricities.
|
||
Check the <a href="#how-to-use" class="ref-to-paragraph">How to use</a> section below for instructions. <br>
|
||
It <strong>does not guarantee fully feasible or reasonable trajectories</strong>. The reasons and details
|
||
are explained in the <a href="#issues" class="ref-to-paragraph">Current issues</a> section.
|
||
For any questions or remarks, see the
|
||
<a href="https://forum.kerbalspaceprogram.com/index.php?/topic/204391-online-calculator-for-trajectories-with-multiple-gravity-assists/">
|
||
forum post</a>.
|
||
</p>
|
||
</header>
|
||
|
||
<section id="main-content">
|
||
<section id="calculator-panel">
|
||
<h2 class="calculation-panel-title">Calculator</h2>
|
||
<h5 class="calculation-subtitle">Flyby sequence configuration</h5>
|
||
<!-- Start of flyby sequence configuration panel -->
|
||
<div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="origin-selector">Origin:</label>
|
||
<div class="controls">
|
||
<select id="origin-selector" name="origin-selector" class="planet-selector">
|
||
<!-- filled by js -->
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="destination-selector">Destination:</label>
|
||
<div class="controls">
|
||
<select id="destination-selector" name="destination-selecto" class="planet-selector">
|
||
<!-- filled by js -->
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label class="control-label" for="max-swingbys">Max swing-bys:</label>
|
||
<div class="controls">
|
||
<input class="number-input" name="max-swingbys" id="max-swingbys" type="number" value="2" min="0" max="10">
|
||
</div>
|
||
</div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="max-resonant-swingbys">Max resonances:</label>
|
||
<div class="controls">
|
||
<input class="number-input" name="max-resonant-swingbys" id="max-resonant-swingbys" type="number" value="1" min="0" max="10">
|
||
</div>
|
||
</div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="max-back-legs">Max back legs:</label>
|
||
<div class="controls">
|
||
<input class="number-input" name="max-back-legs" id="max-back-legs" type="number" value="1" min="0" max="10">
|
||
</div>
|
||
</div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="max-back-spacing">Max back spacing:</label>
|
||
<div class="controls">
|
||
<input class="number-input" name="max-back-spacing" id="max-back-spacing" type="number" value="1" min="0" max="10">
|
||
</div>
|
||
</div>
|
||
<!-- Sequence generation buttons and messages -->
|
||
|
||
<div class="form-actions" id="sequence-generator">
|
||
<button class="submit-btn" id="sequence-btn">Generate sequences</button>
|
||
<button class="stop-btn" id="sequence-stop-btn">Stop</button>
|
||
</div>
|
||
<p class="progress-msg" id="sequence-progress" hidden></p>
|
||
<p class="error-msg" id="sequence-params-error" hidden><strong>Error:</strong> <span></span></p>
|
||
|
||
<div class="control-group">
|
||
<label class="control-label" for="sequence-selector">Selected sequence:</label>
|
||
<div class="controls">
|
||
<select name="sequence-selector" id="sequence-selector" disabled>
|
||
<!-- filled by js -->
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group" id="custom-sequence-group">
|
||
<label class="control-label" for="custom-sequence">Custom sequence:</label>
|
||
<div class="controls">
|
||
<input type="text" id="custom-sequence" name="custom-sequence" placeholder="Leave empty to ignore">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- End of flyby sequence configuration panel -->
|
||
|
||
<h5 class="calculation-subtitle" id="dep-params-subtitle">Departure / Arrival parameters</h5>
|
||
<!-- Start of trajectory optimization configuration panel -->
|
||
<div>
|
||
<div class="control-group time-selector-group">
|
||
<label class="control-label">Earliest departure:</label>
|
||
<div class="controls" id="start-date">
|
||
<div class="time-selector" id="start-time">
|
||
<div class="time-input">
|
||
<label for="start-year">Year</label>
|
||
<input type="number" value="1" name="start-year" id="start-year">
|
||
</div>
|
||
<div class="time-input">
|
||
<label for="start-day">Day</label>
|
||
<input type="number" value="1" name="start-day" id="start-day">
|
||
</div>
|
||
<div class="time-input last-time-input">
|
||
<label for="start-hour">Hour</label>
|
||
<input type="number" value="0" name="start-hour" id="start-hour">
|
||
</div>
|
||
</div>
|
||
<!-- End of start date input -->
|
||
</div>
|
||
</div>
|
||
<div class="control-group time-selector-group">
|
||
<label class="control-label">Latest departure:</label>
|
||
<div class="controls" id="end-date">
|
||
<div class="time-selector" id="end-time">
|
||
<div class="time-input">
|
||
<label for="end-year">Year</label>
|
||
<input type="number" value="1" name="end-year" id="end-year">
|
||
</div>
|
||
<div class="time-input">
|
||
<label for="end-day">Day</label>
|
||
<input type="number" value="1" name="end-day" id="end-day">
|
||
</div>
|
||
<div class="time-input last-time-input">
|
||
<label for="end-hour">Hour</label>
|
||
<input type="number" value="0" name="end-hour" id="end-hour">
|
||
</div>
|
||
</div>
|
||
<!-- End of end date input -->
|
||
</div>
|
||
</div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="start-altitude">Departure altitude:</label>
|
||
<div class="controls">
|
||
<input class="number-input" name="start-altitude" id="start-altitude" type="number" value="0">
|
||
km (above surface level)
|
||
</div>
|
||
</div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="end-altitude">Destination altitude:</label>
|
||
<div class="controls">
|
||
<input class="number-input" name="end-altitude" id="end-altitude" type="number" value="0">
|
||
km (above surface level)
|
||
</div>
|
||
</div>
|
||
<div class="control-group">
|
||
<label class="control-label" for="max-duration">Max duration:</label>
|
||
<div class="controls">
|
||
<input class="number-input" name="max-duration" id="max-duration" type="number" value="0" min="1">
|
||
days
|
||
<input name="use-max-duration" id="use-max-duration" type="checkbox">
|
||
</div>
|
||
</div>
|
||
<div id="insertion-checkbox-container">
|
||
<div class="controls">
|
||
<input name="insertion-checkbox" id="insertion-checkbox" type="checkbox">
|
||
<label for="insertion-checkbox">No insertion burn</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sequence optimization buttons and messages -->
|
||
<div class="form-actions">
|
||
<button class="submit-btn" id="search-btn">Search trajectory</button>
|
||
<button class="stop-btn" id="search-stop-btn">Stop</button>
|
||
</div>
|
||
<p class="progress-msg" id="search-progress" hidden></p>
|
||
<p class="error-msg" id="search-params-error" hidden><strong>Error:</strong> <span></span></p>
|
||
|
||
<!-- Chart container -->
|
||
<div id="evolution-plot-container">
|
||
<canvas id="evolution-plot"></canvas>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="main-panel">
|
||
<!-- System rendering controls -->
|
||
<div id="system-display">
|
||
<div id="canvas-top-bar">
|
||
<div class="time-selector" id="system-time">
|
||
<div class="time-input">
|
||
<label for="system-year">Year</label>
|
||
<input type="number" value="1" name="system-year" id="system-year">
|
||
</div>
|
||
<div class="time-input">
|
||
<label for="system-day">Day</label>
|
||
<input type="number" value="1" name="system-day" id="system-day">
|
||
</div>
|
||
<div class="time-input last-time-input">
|
||
<label for="system-hour">Hour</label>
|
||
<input type="number" value="0" name="system-hour" id="system-hour">
|
||
</div>
|
||
</div>
|
||
<select id="system-selector" name="system-selector">
|
||
<!-- filled by JS -->
|
||
</select>
|
||
</div>
|
||
|
||
<canvas id="three-canvas" width="750" height="550"></canvas>
|
||
|
||
<div id="system-control-infos">
|
||
<p>
|
||
<strong>Mouse drag:</strong> rotate -
|
||
<strong>Double click:</strong> focus on body -
|
||
<strong>Mouse wheel:</strong> zoom
|
||
</p>
|
||
<div id="soi-toggle-box">
|
||
<input type="checkbox" id="soi-checkbox" name="soi-checkbox">
|
||
<label for="soi-checkbox">Show SOIs</label>
|
||
|
||
<input type="checkbox" id="date-as-elapsed-checkbox" name="date-as-elapsed-checkbox">
|
||
<label for="date-as-elapsed-checkbox">Show dates as elapsed time</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- End of the 3D display panel -->
|
||
|
||
<div id="result-panel">
|
||
<div id="result-panel-header">
|
||
<div id="result-panel-header-left">
|
||
<h2 class="calculation-panel-title">Calculated trajectory</h2>
|
||
<button id="show-text-btn" class="submit-btn" disabled>
|
||
<i class="fa-solid fa-circle-info"></i> Details
|
||
</button>
|
||
<button id="download-csv-btn" class="submit-btn" disabled>
|
||
<i class="fa-solid fa-file-arrow-down"></i> Data
|
||
</button>
|
||
</div>
|
||
<div id="steps-slider-control-group" class="control-group">
|
||
<label class="control-label" for="displayed-steps-slider">Displayed steps:</label>
|
||
<div id="steps-slider-controls" class="controls">
|
||
<input name="displayed-steps-slider" id="displayed-steps-slider" type="range" min="0" max="1" value="1" disabled>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="result-sub-panels">
|
||
<div id="result-controls">
|
||
|
||
<div class="control-group">
|
||
<label class="control-label">Departure date:</label>
|
||
<p><strong><span id="result-departure-date" class="clickable-date">--</span> UT</strong></p>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label class="control-label">Arrival date:</label>
|
||
<p><strong><span id="result-arrival-date" class="clickable-date">--</span> UT</strong></p>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label class="control-label">Total ΔV:</label>
|
||
<p><strong><span id="result-total-delta-v">--</span> m/s</strong></p>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label class="control-label" for="details-selector">Maneuvers:</label>
|
||
<div class="controls">
|
||
<select id="details-selector" name="details-selector" disabled>
|
||
<!-- filled by js -->
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<!-- End of result controls -->
|
||
|
||
<div>
|
||
<div id="maneuvre-details" class="result-details">
|
||
<h3>Maneuver <span id="maneuvre-number"></span> details:</h3>
|
||
<ul>
|
||
<li><strong>Date:</strong><span id="maneuvre-date" class="clickable-date">--</span> MET</li>
|
||
<li>
|
||
<div id="maneuver-dv-li">
|
||
<strong>ΔV (m/s):</strong>
|
||
<table id="maneuver-dv-table">
|
||
<tbody>
|
||
<tr>
|
||
<th class="prograde-label">prograde</th>
|
||
<th class="normal-label">normal</th>
|
||
<th class="radial-label">radial</th>
|
||
</tr>
|
||
<tr>
|
||
<td><span id="prograde-delta-v">--</span></td>
|
||
<td><span id="normal-delta-v">--</span></td>
|
||
<td><span id="radial-delta-v">--</span></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</li>
|
||
<li hidden><strong>Ejection angle:</strong><span id="ejection-angle">--</span>° (counter clockwise)</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="flyby-details" class="result-details" hidden>
|
||
<h3>Flyby <span id="flyby-number"></span> details:</h3>
|
||
<ul>
|
||
<li><strong>SOI enter date:</strong> <span id="flyby-start-date" class="clickable-date">--</span> MET</li>
|
||
<li><strong>SOI exit date:</strong> <span id="flyby-end-date" class="clickable-date">--</span> MET</li>
|
||
<li><strong>Periapsis altitude:</strong> <span id="flyby-periapsis-altitude">--</span> km</li>
|
||
<li><strong>Inclination:</strong> <span id="flyby-inclination">--</span>°</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<!-- End of maneuvre details -->
|
||
</div>
|
||
</div>
|
||
<!-- End of result panel -->
|
||
|
||
<article id="how-to-use">
|
||
<h2>How to use</h2>
|
||
<p>
|
||
Use the calculator on the left hand side to configure the settings of the trajectory you
|
||
are looking for.
|
||
The trajectory determination is done through the two following steps:
|
||
</p>
|
||
<ol>
|
||
<li>Determining the planetary sequence that the trajectory must follow.</li>
|
||
<li>Calculating the optimal trajectory using that sequence.</li>
|
||
</ol>
|
||
<p>
|
||
The nomenclature and the configuration of the calculator is further described in the following paragraphs.
|
||
</p>
|
||
|
||
<h3>Planetary sequence</h3>
|
||
<p>
|
||
The flyby sequence is the sequence of bodies encountered during the mission's
|
||
flight time. It starts with the departure body and ends with the destination body.
|
||
All intermediate bodies of the sequence are used for gravity assist. <br>
|
||
Sequences are represented using the first characters (at least two) of each body's name. For example, a mission
|
||
going from Kerbin to Jool, with a first flyby of Eve followed by a flyby of Duna would be written:
|
||
Ke-Ev-Du-Jo.
|
||
</p>
|
||
|
||
<h3>Interplanetary legs</h3>
|
||
<p>
|
||
Each planet-to-planet transfer is called a <em>leg</em>, and consists of 3 steps:
|
||
</p>
|
||
<ol>
|
||
<li>An unpowered flight from the exit of the last encountered body's sphere of influence.</li>
|
||
<li>A deep space maneuver (DSM) used to correct the path to aim at the next body in the sequence.</li>
|
||
<li>An unpowered flight until entering the next body's sphere of influence.</li>
|
||
</ol>
|
||
|
||
<h3>Sequence generation</h3>
|
||
<p>
|
||
The first step of the trajectory planning consists of generating a planetary sequence.
|
||
It will list a bunch of <em>possible</em> sequences considering a very simplified model of the solar system
|
||
and swing-bys, without any phasing.
|
||
Therefore there is <strong>no guarantee</strong> to have the most optimal sequence generated. But it tries to
|
||
provide feasible sequences. <br>
|
||
</p>
|
||
<p>
|
||
Alternatively, it is possible to set a custom sequence if no interesting sequences is proposed by the generator,
|
||
or to define custom routes. The input text must be a valid sequence representation.
|
||
The trajectory calculation step will choose the custom sequence first if it is defined.
|
||
</p>
|
||
<p>
|
||
For the sequence generator, the following settings must be defined:
|
||
</p>
|
||
<ul>
|
||
<li>The <strong>origin</strong> and <strong>destination</strong> body of the trajectory.</li>
|
||
<li>
|
||
<strong>Max swing-bys:</strong> the maximum number of swing bys allowed. Setting this value to 0
|
||
will only provide direct transfer sequences (e.g. Ke-Du).</li>
|
||
<li>
|
||
<strong>Max resonances:</strong> the maximum number of legs starting from and targeting the same body.
|
||
For example the sequence Ke-Ev-Ev-Mo contains one resonance (Ev-Ev).
|
||
</li>
|
||
<li>
|
||
<strong>Max back legs:</strong> the maximum number of legs moving away from the destination body.
|
||
More precisely: if the destination body's orbit
|
||
has a larger radius than the origin body's orbit, then a leg going from a higher orbit to a lower orbit is considered
|
||
a back leg.
|
||
In the same way, if the destination body's orbit has a smaller radius than the origin body's orbit, then a leg going
|
||
from a lower orbit to a higher one is considered a back leg. <br>
|
||
For example, in the sequence Ke-Ev-Ke-Mo, Ev-Ke is a back leg as it gets the spaceship on an orbit farther
|
||
from Moho than the previous leg.
|
||
</li>
|
||
<li>
|
||
<strong>Max back spacing:</strong> the maximum <em>gap</em> between the exited and targeted body
|
||
of a back leg. For example, setting this value to 0 would make the sequence Ke-Ev-Du-Mo forbidden, because
|
||
Ev-Du is a back leg, and a planet with an orbit radius in between the ones of Eve and Duna exists (Kerbin),
|
||
this back leg has a spacing of 1.
|
||
</li>
|
||
</ul>
|
||
|
||
<h3>Trajectory calculation</h3>
|
||
<p>
|
||
Once the planetary sequence is selected, we must specify the departure and arrival conditions:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
<strong>Earliest and latest departure date:</strong> the time range in which the departure date must stay.
|
||
</li>
|
||
<li>
|
||
<strong>Departure and destination altitude:</strong> the altitude of the parking orbit
|
||
around the departure and destination body.
|
||
</li>
|
||
<li>
|
||
<strong>Max duration:</strong> if checked, forces the trajectory solver to try not to exceed the
|
||
specified maximum duration (in number of days). Note that it may give a trajectory that exceeds the
|
||
limit if it cannot find shorter ones.
|
||
</li>
|
||
<li>
|
||
<strong>No insertion burn:</strong> if checked, ignores circularization at the destination body and
|
||
instead do a flyby at arrival.
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
The trajectory search step will then <em>try</em> to find <em>a possible</em> optimal trajectory given the sequence and the
|
||
departure/arrival conditions.
|
||
</p>
|
||
<p>
|
||
The resulting trajectory will be displayed in the interactive 3D window, alongside with
|
||
the details of each maneuver (ejection, DSMs and circularization) and flybys' information.
|
||
</p>
|
||
|
||
<figure id="example-image-container">
|
||
<img src="imgs/example_trajectory_2.png" alt="Ke-Ev-Jo-Ee example trajectory">
|
||
<figcaption>
|
||
An example of a Ke-Ev-Jo-Ee trajectory requiring 1693m/s of ΔV (with Eeloo circularization)
|
||
</figcaption>
|
||
</figure>
|
||
|
||
</article>
|
||
<!-- End of How to use section -->
|
||
|
||
<article id="issues">
|
||
<h2>Current issues</h2>
|
||
<p>
|
||
As mentioned in the introduction, solving MGA trajectories is <em>non trivial</em>, and requires the use
|
||
of iterative optimization methods which <em>approximate</em> the optimal solution.
|
||
Thus this tool has no guarantee of giving the best trajectory, and therefore can propose
|
||
different trajectories for the same input settings. It is recommended to search for several trajectories
|
||
and then choose the best one. It is also advised to look for small launch windows (i.e close earliest
|
||
and latest departure dates) to improve the search.
|
||
</p>
|
||
<p>
|
||
The computed trajectory itself may not be feasible in game because
|
||
this tool may not consider some in game particularities.
|
||
Therefore the maneuvers' details are more indicative and fine tunings are necessary.
|
||
</p>
|
||
<p>
|
||
For the Jool's system, since the SOIs of the satellites are large compared to the radius of their
|
||
orbits, it is possible that a produced trajectory is not feasible because it intersects a satellites's SOI
|
||
before reaching the final state of an arc.
|
||
</p>
|
||
</article>
|
||
<!-- End of Issues section -->
|
||
|
||
</section>
|
||
<!-- End of main panel -->
|
||
|
||
</section>
|
||
|
||
<footer>
|
||
<p>
|
||
Created by <a href="https://github.com/nmisyats">Krafpy</a>. This project is open source and hosted on
|
||
<a href="https://github.com/">Github</a>.
|
||
</p>
|
||
<p>
|
||
Uses <a href="https://threejs.org/">THREE.js</a>, <a href="https://www.chartjs.org/">Chart.js</a>,
|
||
<a href="https://github.com/nodeca/js-yaml">js-yaml</a>, and
|
||
code from ESA's <a href="https://github.com/esa/pykep">pykep</a> library.
|
||
</p>
|
||
<p>
|
||
The design of this website was inspired from Olex's
|
||
<a href="https://ksp.olex.biz/">Interactive illustrated interplanetary guide and calculator for KSP</a>.
|
||
</p>
|
||
<p>
|
||
KSP, Kerbal Space Program and all the planet/moon names are property of Squad.
|
||
</p>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<!-- template -->
|
||
<!-- hidden style="display: none;" -->
|
||
<div id="draggable-text-template" class="draggable-text-box" hidden style="display: none;">
|
||
<div class="draggable-text-header">
|
||
<h3 class="draggable-text-title"></h3>
|
||
<div>
|
||
<button class="draggable-copy-btn"><i class="fa-regular fa-clipboard"></i></button>
|
||
<button class="draggable-close-btn"><i class="fa fa-close"></i></button>
|
||
</div>
|
||
</div>
|
||
<textarea name="draggable-textaera" cols="70" rows="30" readonly></textarea>
|
||
</div>
|
||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||
<script src="includes/LineSegmentGeometry.js"></script>
|
||
<script src="includes/LineSegments2.js"></script>
|
||
<script src="includes/LineGeometry.js"></script>
|
||
<script src="includes/LineMaterial.js"></script>
|
||
<script src="includes/Line2.js"></script>
|
||
<script src="includes/OrbitControls.js"></script>
|
||
<script src="dist/main/main.js" type="module"></script>
|
||
</body>
|
||
</html> |