Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/launchpoint/ui/static/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ function cacheElements() {
"top3dButton",
"mapEmptyState",
"mapEmptyStateText",
"loadExampleButton",
].forEach((id) => {
el[id] = document.getElementById(id);
});
Expand Down Expand Up @@ -221,6 +222,7 @@ function bindEvents() {
}
});
el.importButton.addEventListener("click", () => el.fileInput.click());
el.loadExampleButton?.addEventListener("click", loadExampleSightings);
el.fileInput.addEventListener("change", handleImport);
el.clearButton.addEventListener("click", clearSightings);
el.runButton.addEventListener("click", runAnalysis);
Expand Down Expand Up @@ -399,6 +401,23 @@ function normalizeSighting(raw, index) {
};
}

// Mirrors examples/sightings_zurich.json so a first-time visitor can see a real
// result without having to find input data first.
const EXAMPLE_SIGHTINGS = [
{ label: "north pass", lat: 47.3905, lon: 8.551, altitude: 110, position_sigma_m: 200, altitude_sigma_m: 25 },
{ label: "east pass", lat: 47.3802, lon: 8.572, altitude: 95, position_sigma_m: 250, altitude_sigma_m: 30 },
{ label: "south pass", lat: 47.3701, lon: 8.5505, altitude: 130, position_sigma_m: 220, altitude_sigma_m: 25 },
];

function loadExampleSightings() {
state.sightings = EXAMPLE_SIGHTINGS.map(normalizeSighting);
state.selectedSighting = 0;
renderSightings();
renderMarkers();
fitSightings();
updateRunReadiness();
}

function renderSightings() {
el.sightingCount.textContent = String(state.sightings.length);
el.sightingList.innerHTML = "";
Expand Down Expand Up @@ -1544,6 +1563,8 @@ function updateMapEmptyState() {
? "Draw a flight area on the map to find the best launch site for its coverage."
: "Add a sighting on the map, or import a JSON/CSV file, to estimate the operator's location.";
}
// The bundled example is a set of sightings, so it only applies to the origin finder.
if (el.loadExampleButton) el.loadExampleButton.hidden = state.mode === "coverage";
}

function statusText() {
Expand Down
4 changes: 4 additions & 0 deletions src/launchpoint/ui/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ <h2>Analysis</h2>
<div id="mapEmptyState" class="empty-state">
<strong>Start an analysis</strong>
<span id="mapEmptyStateText">Add a sighting on the map, or import a JSON/CSV file, to estimate the operator's location.</span>
<button id="loadExampleButton" class="control-button primary" type="button">
<i data-lucide="play"></i>
<span>Load Zürich example</span>
</button>
</div>
<div class="map-legend" aria-label="Heatmap legend">
<span>Low</span>
Expand Down
13 changes: 13 additions & 0 deletions src/launchpoint/ui/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,19 @@ input[type="range"] {
font-size: 14px;
}

/* The card itself is click-through so it never blocks the map, but its own
controls must stay interactive. */
.empty-state button {
margin-top: 6px;
pointer-events: auto;
}

/* .control-button sets display:inline-flex, which would override the plain
[hidden] attribute — restore hiding for the mode-gated example button. */
.empty-state button[hidden] {
display: none;
}

.empty-state.hidden {
display: none;
}
Expand Down
Loading