[Migration] NPM (#1266)

* chore: add npm + vite for progressive enhancement

* fix: update Dockerfile to copy only the dist folder contents

* fix: update Dockerfile to use multi-stage build for optimized production image

* fix: correct nginx config file copy command in Dockerfile

* chore: add netlify configuration for build and redirects

* fix: add NODE_VERSION to environment in Netlify configuration

* remove wrong dist folder

* Update package.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: split public and src

* migrating all util files from js to ts

* feat: Implement HeightmapGenerator and Voronoi module

- Added HeightmapGenerator class for generating heightmaps with various tools (Hill, Pit, Range, Trough, Strait, etc.).
- Introduced Voronoi class for creating Voronoi diagrams using Delaunator.
- Updated index.html to include new modules.
- Created index.ts to manage module imports.
- Enhanced arrayUtils and graphUtils with type definitions and improved functionality.
- Added utility functions for generating grids and calculating Voronoi cells.

* chore: add GitHub Actions workflow for deploying to GitHub Pages

* fix: update branch name in GitHub Actions workflow from 'main' to 'master'

* chore: update package.json to specify Node.js engine version and remove unused launch.json

* Initial plan

* Update copilot guidelines to reflect NPM/Vite/TypeScript migration

Co-authored-by: Azgaar <26469650+Azgaar@users.noreply.github.com>

* Update src/modules/heightmap-generator.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/utils/graphUtils.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/modules/heightmap-generator.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* feat: Add TIME and ERROR variables to global scope in HeightmapGenerator

* fix: Update base path in vite.config.ts for Netlify deployment

* fix: Update Node.js version in Dockerfile to 24-alpine

---------

Co-authored-by: Marc Emmanuel <marc.emmanuel@tado.com>
Co-authored-by: Marc Emmanuel <marcwissler@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Azgaar <26469650+Azgaar@users.noreply.github.com>
This commit is contained in:
Azgaar 2026-01-22 12:20:12 +01:00 committed by GitHub
parent 0c26f0831f
commit 9e0eb03618
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
713 changed files with 5182 additions and 2161 deletions

View file

@ -1,319 +0,0 @@
const initialSeed = generateSeed();
let graph = getGraph(grid);
appendStyleSheet();
insertHtml();
addListeners();
export function open() {
closeDialogs(".stable");
const $templateInput = byId("templateInput");
setSelected($templateInput.value);
graph = getGraph(graph);
$("#heightmapSelection").dialog({
title: "Select Heightmap",
resizable: false,
position: {my: "center", at: "center", of: "svg"},
buttons: {
Cancel: function () {
$(this).dialog("close");
},
Select: function () {
const id = getSelected();
applyOption($templateInput, id, getName(id));
lock("template");
$(this).dialog("close");
},
"New Map": function () {
const id = getSelected();
applyOption($templateInput, id, getName(id));
lock("template");
const seed = getSeed();
regeneratePrompt({seed, graph});
$(this).dialog("close");
}
}
});
}
function appendStyleSheet() {
const style = document.createElement("style");
style.textContent = /* css */ `
div.dialog > div.heightmap-selection {
width: 70vw;
height: 70vh;
}
.heightmap-selection_container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
grid-gap: 6px;
}
@media (max-width: 600px) {
.heightmap-selection_container {
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
grid-gap: 4px;
}
}
@media (min-width: 2000px) {
.heightmap-selection_container {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-gap: 8px;
}
}
.heightmap-selection_options {
display: grid;
grid-template-columns: 2fr 1fr;
}
.heightmap-selection_options > div:first-child {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
align-items: center;
justify-self: start;
justify-items: start;
}
@media (max-width: 600px) {
.heightmap-selection_options {
grid-template-columns: 3fr 1fr;
}
.heightmap-selection_options > div:first-child {
display: block;
}
}
.heightmap-selection_options > div:last-child {
justify-self: end;
}
.heightmap-selection article {
padding: 4px;
border-radius: 8px;
transition: all 0.1s ease-in-out;
filter: drop-shadow(1px 1px 4px #999);
}
.heightmap-selection article:hover {
background-color: #ddd;
filter: drop-shadow(1px 1px 8px #999);
cursor: pointer;
}
.heightmap-selection article.selected {
background-color: #ccc;
outline: 1px solid var(--dark-solid);
filter: drop-shadow(1px 1px 8px #999);
}
.heightmap-selection article > div {
display: flex;
justify-content: space-between;
padding: 2px 1px;
}
.heightmap-selection article > img {
width: 100%;
aspect-ratio: ${graphWidth}/${graphHeight};
border-radius: 8px;
object-fit: fill;
}
.heightmap-selection article .regeneratePreview {
outline: 1px solid #bbb;
padding: 1px 3px;
border-radius: 4px;
transition: all 0.1s ease-in-out;
}
.heightmap-selection article .regeneratePreview:hover {
outline: 1px solid #666;
}
.heightmap-selection article .regeneratePreview:active {
outline: 1px solid #333;
color: #000;
transform: rotate(45deg);
}
`;
document.head.appendChild(style);
}
function insertHtml() {
const heightmapColorSchemeOptions = Object.keys(heightmapColorSchemes)
.map(scheme => `<option value="${scheme}">${scheme}</option>`)
.join("");
const heightmapSelectionHtml = /* html */ `<div id="heightmapSelection" class="dialog stable">
<div class="heightmap-selection">
<section data-tip="Select heightmap template template provides unique, but similar-looking maps on generation">
<header><h1>Heightmap templates</h1></header>
<div class="heightmap-selection_container"></div>
</section>
<section data-tip="Select precreated heightmap it will be the same for each map">
<header><h1>Precreated heightmaps</h1></header>
<div class="heightmap-selection_container"></div>
</section>
<section>
<header><h1>Options</h1></header>
<div class="heightmap-selection_options">
<div>
<label data-tip="Rerender all preview images" class="checkbox-label" id="heightmapSelectionRedrawPreview">
<i class="icon-cw"></i>
Redraw preview
</label>
<div>
<input id="heightmapSelectionRenderOcean" class="checkbox" type="checkbox" />
<label data-tip="Draw heights of water cells" for="heightmapSelectionRenderOcean" class="checkbox-label">Render ocean heights</label>
</div>
<div data-tip="Color scheme used for heightmap preview">
Color scheme
<select id="heightmapSelectionColorScheme">${heightmapColorSchemeOptions}</select>
</div>
</div>
<div>
<button data-tip="Open Template Editor" data-tool="templateEditor" id="heightmapSelectionEditTemplates">Edit Templates</button>
<button data-tip="Open Image Converter" data-tool="imageConverter" id="heightmapSelectionImportHeightmap">Import Heightmap</button>
</div>
</div>
</section>
</div>
</div>`;
byId("dialogs").insertAdjacentHTML("beforeend", heightmapSelectionHtml);
const sections = document.getElementsByClassName("heightmap-selection_container");
sections[0].innerHTML = Object.keys(heightmapTemplates)
.map(key => {
const name = heightmapTemplates[key].name;
Math.random = aleaPRNG(initialSeed);
const heights = HeightmapGenerator.fromTemplate(graph, key);
return /* html */ `<article data-id="${key}" data-seed="${initialSeed}">
<img src="${getHeightmapPreview(heights)}" alt="${name}" />
<div>
${name}
<span data-tip="Regenerate preview" class="icon-cw regeneratePreview"></span>
</div>
</article>`;
})
.join("");
sections[1].innerHTML = Object.keys(precreatedHeightmaps)
.map(key => {
const name = precreatedHeightmaps[key].name;
drawPrecreatedHeightmap(key);
return /* html */ `<article data-id="${key}" data-seed="${initialSeed}">
<img alt="${name}" />
<div>${name}</div>
</article>`;
})
.join("");
}
function addListeners() {
byId("heightmapSelection").on("click", event => {
const article = event.target.closest("#heightmapSelection article");
if (!article) return;
const id = article.dataset.id;
if (event.target.matches("span.icon-cw")) regeneratePreview(article, id);
setSelected(id);
});
byId("heightmapSelectionRenderOcean").on("change", redrawAll);
byId("heightmapSelectionColorScheme").on("change", redrawAll);
byId("heightmapSelectionRedrawPreview").on("click", redrawAll);
byId("heightmapSelectionEditTemplates").on("click", confirmHeightmapEdit);
byId("heightmapSelectionImportHeightmap").on("click", confirmHeightmapEdit);
}
function getSelected() {
return byId("heightmapSelection").querySelector(".selected")?.dataset?.id;
}
function setSelected(id) {
const $heightmapSelection = byId("heightmapSelection");
$heightmapSelection.querySelector(".selected")?.classList?.remove("selected");
$heightmapSelection.querySelector(`[data-id="${id}"]`)?.classList?.add("selected");
}
function getSeed() {
return byId("heightmapSelection").querySelector(".selected")?.dataset?.seed;
}
function getName(id) {
const isTemplate = id in heightmapTemplates;
return isTemplate ? heightmapTemplates[id].name : precreatedHeightmaps[id].name;
}
function getGraph(currentGraph) {
const newGraph = shouldRegenerateGrid(currentGraph, seed) ? generateGrid() : deepCopy(currentGraph);
delete newGraph.cells.h;
return newGraph;
}
function drawTemplatePreview(id) {
const heights = HeightmapGenerator.fromTemplate(graph, id);
const dataUrl = getHeightmapPreview(heights);
const article = byId("heightmapSelection").querySelector(`[data-id="${id}"]`);
article.querySelector("img").src = dataUrl;
}
async function drawPrecreatedHeightmap(id) {
const heights = await HeightmapGenerator.fromPrecreated(graph, id);
const dataUrl = getHeightmapPreview(heights);
const article = byId("heightmapSelection").querySelector(`[data-id="${id}"]`);
article.querySelector("img").src = dataUrl;
}
function regeneratePreview(article, id) {
graph = getGraph(graph);
const seed = generateSeed();
article.dataset.seed = seed;
Math.random = aleaPRNG(seed);
drawTemplatePreview(id);
}
function redrawAll() {
graph = getGraph(graph);
const articles = byId("heightmapSelection").querySelectorAll(`article`);
for (const article of articles) {
const {id, seed} = article.dataset;
Math.random = aleaPRNG(seed);
const isTemplate = id in heightmapTemplates;
if (isTemplate) drawTemplatePreview(id);
else drawPrecreatedHeightmap(id);
}
}
function confirmHeightmapEdit() {
const tool = this.dataset.tool;
confirmationDialog({
title: this.dataset.tip,
message: "Opening the tool will erase the current map. Are you sure you want to proceed?",
confirm: "Continue",
onConfirm: () => editHeightmap({mode: "erase", tool})
});
}
function getHeightmapPreview(heights) {
const scheme = getColorScheme(byId("heightmapSelectionColorScheme").value);
const renderOcean = byId("heightmapSelectionRenderOcean").checked;
const dataUrl = drawHeights({heights, width: graph.cellsX, height: graph.cellsY, scheme, renderOcean});
return dataUrl;
}