diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index b027f814..f01a2390 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms - +github: Azgaar patreon: Azgaar diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..698d23ae --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,89 @@ +# Fantasy Map Generator + +Azgaar's Fantasy Map Generator is a client-side JavaScript web application for creating fantasy maps. It generates detailed fantasy worlds with countries, cities, rivers, biomes, and cultural elements. + +Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here. + +## Working Effectively + +- **CRITICAL**: This is a static web application - NO build process needed. No npm install, no compilation, no bundling. +- Run the application using HTTP server (required - cannot run with file:// protocol): + - `python3 -m http.server 8000` - takes 2-3 seconds to start +- Access at: `http://localhost:8000` + +## Validation + +- Always manually validate any changes by: + 1. Starting the HTTP server (NEVER CANCEL - wait for full startup) + 2. Navigate to the application in browser + 3. Click the "►" button to open the menu and generate a new map + 4. **CRITICAL VALIDATION**: Verify the map generates with countries, cities, roads, and geographic features + 5. Test UI interaction: click "Layers" button, verify layer controls work + 6. Test regeneration: click "New Map!" button, verify new map generates correctly +- **Known Issues**: Google Analytics and font loading errors are normal (blocked external resources) + +## Repository Structure + +### Core Files + +- `index.html` - Main application entry point +- `main.js` - Core application logic +- `versioning.js` - Version management and update handling + +### Key Directories + +- `modules/` - core functionality modules: + - `modules/ui/` - UI components (editors, tools, style management) + - `modules/dynamic/` - runtime modules (export, installation) + - `modules/renderers/` - drawing and rendering logic +- `utils/` - utility libraries (math, arrays, strings, etc.) +- `styles/` - visual style presets (JSON files) +- `libs/` - Third-party libraries (D3.js, TinyMCE, etc.) +- `images/` - backgrounds, UI elements +- `charges/` - heraldic symbols and coat of arms elements +- `config/` - Heightmap templates and configurations +- `heightmaps/` - Terrain generation data + +## Common Tasks + +### Making Code Changes + +1. Edit JavaScript files directly (no compilation needed) +2. Refresh browser to see changes immediately +3. **ALWAYS test map generation** after making changes +4. Update version in `versioning.js` for all changes +5. Update file hashes in `index.html` for changed files (format: `file.js?v=1.108.1`) + +### Debugging Map Generation + +- Open browser developer tools console +- Look for timing logs, e.g. "TOTAL: ~0.76s" +- Map generation logs show each step (heightmap, rivers, states, etc.) +- Error messages will indicate specific generation failures + +### Testing Different Map Types + +- Use "New Map!" button for quick regeneration +- Access "Layers" menu to change map visualization +- Available presets: Political, Cultural, Religions, Biomes, Heightmap, Physical, Military + +## Troubleshooting + +### Application Won't Load + +- Ensure using HTTP server (not file://) +- Check console for JavaScript errors +- Verify all files are present in repository + +### Map Generation Fails + +- Check browser console for error messages +- Look for specific module failures in generation logs +- Try refreshing page and generating new map + +### Performance Issues + +- Map generation should complete in ~1 second for standard configurations +- If slower, check browser console for errors + +Remember: This is a sophisticated client-side application that generates complete fantasy worlds with political systems, geography, cultures, and detailed cartographic elements. Always validate that your changes preserve the core map generation functionality. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b254d0ad..acedeb18 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,8 +14,8 @@ # Versioning - diff --git a/components/slider-input.js b/components/slider-input.js new file mode 100644 index 00000000..f1732027 --- /dev/null +++ b/components/slider-input.js @@ -0,0 +1,78 @@ +{ + const style = /* css */ ` + slider-input { + display: flex; + align-items: center; + gap: .4em; + } + `; + + const styleElement = document.createElement("style"); + styleElement.setAttribute("type", "text/css"); + styleElement.innerHTML = style; + document.head.appendChild(styleElement); +} + +{ + const template = document.createElement("template"); + template.innerHTML = /* html */ ` + + + `; + + class SliderInput extends HTMLElement { + constructor() { + super(); + this.appendChild(template.content.cloneNode(true)); + + const range = this.querySelector("input[type=range]"); + const number = this.querySelector("input[type=number]"); + + range.value = number.value = this.value || this.getAttribute("value") || 50; + range.min = number.min = this.getAttribute("min") || 0; + range.max = number.max = this.getAttribute("max") || 100; + range.step = number.step = this.getAttribute("step") || 1; + + range.addEventListener("input", this.handleEvent.bind(this)); + number.addEventListener("input", this.handleEvent.bind(this)); + range.addEventListener("change", this.handleEvent.bind(this)); + number.addEventListener("change", this.handleEvent.bind(this)); + } + + handleEvent(e) { + const value = e.target.value; + const isNaN = Number.isNaN(Number(value)); + if (isNaN || value === "") return e.stopPropagation(); + + const range = this.querySelector("input[type=range]"); + const number = this.querySelector("input[type=number]"); + this.value = range.value = number.value = value; + + this.dispatchEvent( + new CustomEvent(e.type, { + detail: {value}, + bubbles: true, + composed: true + }) + ); + } + + set value(value) { + const range = this.querySelector("input[type=range]"); + const number = this.querySelector("input[type=number]"); + range.value = number.value = value; + } + + get value() { + const number = this.querySelector("input[type=number]"); + return number.value; + } + + get valueAsNumber() { + const number = this.querySelector("input[type=number]"); + return number.valueAsNumber; + } + } + + customElements.define("slider-input", SliderInput); +} diff --git a/icons.css b/icons.css index ae40ee00..4740f091 100644 --- a/icons.css +++ b/icons.css @@ -253,7 +253,7 @@ .icon-coa:before {content:'\f3ed'; font-size: .9em; color: #999;} /* '' */ .icon-half:before {font-weight: bold;content:'½';} .icon-voice:before {content:'🔊';} - +.icon-robot:before {content:'🤖';} .icon-die:before {content:'🎲';} .icon-button-die:before {content:'🎲'; padding-right: .4em;} .icon-button-power:before {content:'💪'; padding-right: .6em;} diff --git a/index.css b/index.css index 555b650c..373bf63c 100644 --- a/index.css +++ b/index.css @@ -122,10 +122,6 @@ a { fill: none; } -#biomes { - stroke-width: 0.7; -} - #landmass { mask: url(#land); fill-rule: evenodd; @@ -170,6 +166,7 @@ t, #texture, #landmass, #vignette, +#gridOverlay, #fogging { pointer-events: none; } @@ -190,20 +187,12 @@ t, font-size: 0.8em; } -#statesBody { - stroke-width: 3; -} - #statesHalo { fill: none; stroke-linecap: round; stroke-linejoin: round; } -#provincesBody { - stroke-width: 0.2; -} - #statesBody, #provincesBody, #relig, @@ -356,6 +345,14 @@ text.drag { font-weight: bold; } +button.ui-button:disabled { + filter: brightness(0.95); +} + +button.ui-button:disabled:hover { + cursor: default; +} + .ui-dialog, #optionsContainer { user-select: none; @@ -525,7 +522,53 @@ input[type="color"]::-webkit-color-swatch-wrapper { font-size: smaller; } +#options input[type="text"] { + border: 0px; + width: 62%; + font-size: smaller; +} + +#options output { + text-align: right; + font-size: smaller; +} + +#options input[type="number"] { + font-size: 0.8em; + border: 0; + text-align: right; + background-color: transparent; + width: 3.3em; +} + +#options input[type="number"]::-webkit-inner-spin-button, +#options input[type="number"]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +#options input[type="number"] { + appearance: textfield; + -moz-appearance: textfield; +} + +#options input[type="number"]:hover { + outline: 1px solid var(--dark-solid); +} + +#options input.paired { + text-align: center; + background-color: white; +} + +#options input.long { + width: 100%; + background-color: white; + text-align: left; +} + #options input[type="range"] { + width: 100%; height: 8px; background: 0; appearance: none; @@ -568,55 +611,7 @@ input[type="color"]::-webkit-color-swatch-wrapper { height: 2px; } -#options input[type="number"] { - font-size: 0.8em; -} - -#options input[type="text"] { - border: 0px; - width: 62%; - font-size: smaller; -} - -#optionsContent output { - text-align: right; - font-size: smaller; -} - -#optionsContent input[type="number"] { - border: 0; - text-align: right; - background-color: transparent; - width: 3.3em; - -moz-appearance: textfield; -} - -#optionsContent input[type="number"]::-webkit-inner-spin-button, -#optionsContent input[type="number"]::-webkit-outer-spin-button { - -webkit-appearance: none; - margin: 0; -} - -#optionsContent input[type="number"]:hover { - outline: 1px solid var(--dark-solid); -} - -#optionsContent input.paired { - text-align: center; - background-color: white; -} - -#optionsContent input.long { - width: 100%; - background-color: white; - text-align: left; -} - -#optionsContent input[type="range"] { - width: 100%; -} - -#optionsContent select { +#options select { width: 100%; } @@ -641,19 +636,6 @@ input[type="color"]::-webkit-color-swatch-wrapper { transform: translate(0px, 1px); } -#styleElements input[type="range"] { - width: 64%; -} - -#styleElements select { - width: 64%; -} - -#styleElements input[type="number"] { - width: 6em; - border: 0; -} - #styleSelectFont > option { font-size: 2em; } @@ -692,6 +674,7 @@ input[type="color"]::-webkit-color-swatch-wrapper { border: none; padding: 0.45em 0.75em; margin: 0.4em 0; + white-space: nowrap; font-family: var(--monospace); animation: glowing 2s infinite; } @@ -724,9 +707,6 @@ input[type="color"]::-webkit-color-swatch-wrapper { padding: 0.45em 0.75em; margin: 0.35em 0; transition: 0.1s; - font-size: 1em; - text-transform: capitalize; - overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -743,7 +723,7 @@ input[type="color"]::-webkit-color-swatch-wrapper { #toolsContent > .grid { display: grid; - grid-template-columns: repeat(4, 1fr); + grid-template-columns: repeat(3, 1fr); margin: 0.2em 0; } @@ -790,7 +770,7 @@ fieldset { text-overflow: ellipsis; } -.tabcontent .buttonoff { +.tabcontent li.buttonoff { background-color: var(--bg-disabled); color: #444444aa; } @@ -1268,7 +1248,6 @@ i.resetButton:active { padding: 0; height: 2px; background: #d4d4d4; - top: -0.35em; position: relative; appearance: none; -webkit-appearance: none; @@ -1533,20 +1512,6 @@ div.states > .burgCulture { width: 6em; } -div.states .burgPopulation { - width: 4.8em; -} - -div.states .burgType { - width: 3em; -} - -div.states .burgType > span { - padding: 0 1px; - color: #6e5e66; - transition: 0.2s; -} - div.states span.inactive { color: #c6c2c2; } @@ -1844,11 +1809,6 @@ div.editorLine { padding: 0px 3px !important; } -#unitsBody > div > * { - display: inline-block; - margin-bottom: 0.2em; -} - .unitsHeader { margin: 0.8em 0 0 -1.1em; font-weight: bold; @@ -1860,28 +1820,21 @@ div.editorLine { margin: 6px 0 0 6px; } -#unitsBody > div > div { +#unitsBody label { + display: inline-block; width: 9em; } -#unitsBody > div > input[type="range"] { - width: 7em; -} - #unitsBody > div > select, #unitsBody > div > input[type="text"] { - width: 12em; -} - -#unitsBody > div > input[type="number"] { - width: 4.35em; -} - -#unitsBody > div > input, -#unitsBody > div > select { + width: 14.4em; border: 1px solid #e9e9e9; } +#unitsBody input[type="range"] { + width: 9em; +} + #unitsEditor i.icon-lock-open, #unitsEditor i.icon-lock { color: #626573; @@ -2414,6 +2367,34 @@ svg.button { margin-left: 0.25em; } +@keyframes clockwiseBorderPulse { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +#chat-widget-container { + user-select: none; +} + +#chat-widget-minimized { + animation: fadeIn 1s ease-in; + transform: scale(0.65); + opacity: var(--bg-opacity); +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: var(--bg-opacity); + } +} + @media print { div, canvas { diff --git a/index.html b/index.html index 6453976a..5a18e579 100644 --- a/index.html +++ b/index.html @@ -11,8 +11,8 @@ name="description" content="Free web app that helps fantasy writers, game masters, and cartographers create and edit fantasy maps" /> - + - + - + - - - - - - - + + + + + + @@ -438,7 +442,7 @@ xture @@ -487,7 +490,6 @@ id="toggleHeight" data-tip="Heightmap: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="H" - class="buttonoff" onclick="toggleHeight(event)" > Heightmap @@ -496,7 +498,6 @@ id="toggleBiomes" data-tip="Biomes: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="B" - class="buttonoff" onclick="toggleBiomes(event)" > Biomes @@ -505,7 +506,6 @@ id="toggleCells" data-tip="Cells structure: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="E" - class="buttonoff" onclick="toggleCells(event)" > Cells @@ -514,7 +514,6 @@ id="toggleGrid" data-tip="Grid: click to toggle, drag to raise or lower. Ctrl + click to edit layer style and select type" data-shortcut="G" - class="buttonoff" onclick="toggleGrid(event)" > Grid @@ -523,7 +522,6 @@ id="toggleCoordinates" data-tip="Coordinate grid: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="O" - class="buttonoff" onclick="toggleCoordinates(event)" > Coordinates @@ -532,7 +530,6 @@ id="toggleCompass" data-tip="Wind (Compass) Rose: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="W" - class="buttonoff" onclick="toggleCompass(event)" > Wind Rose @@ -549,7 +546,6 @@ id="toggleRelief" data-tip="Relief and biome icons: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="F" - class="buttonoff" onclick="toggleRelief(event)" > Relief @@ -558,7 +554,6 @@ id="toggleReligions" data-tip="Religions: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="R" - class="buttonoff" onclick="toggleReligions(event)" > Religions @@ -567,7 +562,6 @@ id="toggleCultures" data-tip="Cultures: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="C" - class="buttonoff" onclick="toggleCultures(event)" > Cultures @@ -584,7 +578,6 @@ id="toggleProvinces" data-tip="Provinces: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="P" - class="buttonoff" onclick="toggleProvinces(event)" > Provinces @@ -593,7 +586,6 @@ id="toggleZones" data-tip="Zones: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="Z" - class="buttonoff" onclick="toggleZones(event)" > Zones @@ -610,17 +602,15 @@ id="toggleRoutes" data-tip="Trade routes: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="U" - class="buttonoff" onclick="toggleRoutes(event)" > Routes
  • Temperature
  • @@ -628,7 +618,6 @@ id="togglePopulation" data-tip="Population map: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="N" - class="buttonoff" onclick="togglePopulation(event)" > Population @@ -637,17 +626,15 @@ id="toggleIce" data-tip="Icebergs and glaciers: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="J" - class="buttonoff" onclick="toggleIce(event)" > Ice
  • Precipitation
  • @@ -655,7 +642,6 @@ id="toggleEmblems" data-tip="Emblems: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="Y" - class="buttonoff" onclick="toggleEmblems(event)" > Emblems @@ -669,10 +655,10 @@ Labels
  • Icons
  • @@ -680,7 +666,6 @@ id="toggleMilitary" data-tip="Military forces: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="M" - class="buttonoff" onclick="toggleMilitary(event)" > Military @@ -689,7 +674,6 @@ id="toggleMarkers" data-tip="Markers: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style" data-shortcut="K" - class="buttonoff" onclick="toggleMarkers(event)" > Markers @@ -698,7 +682,6 @@ id="toggleRulers" data-tip="Rulers: click to toggle, drag to move, click on label to delete. Ctrl + click to edit layer style" data-shortcut="= (equal sign)" - class="buttonoff" onclick="toggleRulers(event)" > Rulers @@ -836,27 +819,24 @@ - + Terracing - - 0 + Reduce layers - - 5 + Simplify line - - 0 + @@ -874,7 +854,7 @@ Color scheme - + @@ -1241,7 +1229,7 @@ - + @@ -1281,16 +1269,14 @@ Fill opacity - - 0.3 + Labels size - - 8 + @@ -1307,8 +1293,7 @@ Body opacity - - + @@ -1326,24 +1311,21 @@ Halo width - - 10 + Halo opacity - - 1 + Halo blur - - 4 + @@ -1352,62 +1334,36 @@ Fill opacity - - 1 + Box Size - - 3 + - State Size + State size - + - Province Size + Province size - + - Burg Size + Burg size - + @@ -1495,15 +1451,14 @@ Back opacity - - + Back fill - + @@ -1511,7 +1466,7 @@ Back stroke - + Width @@ -1573,7 +1528,9 @@ - + - + Year and era @@ -1683,10 +1640,10 @@ Cultures number - + - + @@ -1712,53 +1669,41 @@ - + States number - - - - - + + - + Provinces ratio - - - - - + + - + Size variety - - - - - + + - + Growth rate - - - - - + + @@ -1779,14 +1724,17 @@ data-tip="Define how many organized religions and cults should be generated. Cultures will have their own folk religions in any case" > - + Religions number - - - - - + + @@ -1815,22 +1763,16 @@ > Interface size - - - - - + + Tooltip size - - - - - + + @@ -1850,11 +1792,8 @@ Transparency - - - - - + + @@ -1897,6 +1836,17 @@ + + + Azgaar assistant + + + + + Speaker voice @@ -2060,8 +2010,8 @@ @@ -2145,7 +2095,7 @@ id="regenerateStateLabels" data-tip="Click to update state labels placement based on current borders" > - Labels + State Labels @@ -2239,8 +2189,8 @@
    Create
    - - + +
    @@ -2749,6 +2699,24 @@ /> + + + - + + @@ -3667,10 +3644,10 @@ >
    -
    -
    Emblem:
    - - +
    +
    Emblem:
    +
    +
    @@ -4123,72 +4100,24 @@