diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index f01a2390..b027f814 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 deleted file mode 100644 index 698d23ae..00000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,89 +0,0 @@ -# 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 acedeb18..b254d0ad 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 deleted file mode 100644 index f1732027..00000000 --- a/components/slider-input.js +++ /dev/null @@ -1,78 +0,0 @@ -{ - 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 4740f091..ae40ee00 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 373bf63c..555b650c 100644 --- a/index.css +++ b/index.css @@ -122,6 +122,10 @@ a { fill: none; } +#biomes { + stroke-width: 0.7; +} + #landmass { mask: url(#land); fill-rule: evenodd; @@ -166,7 +170,6 @@ t, #texture, #landmass, #vignette, -#gridOverlay, #fogging { pointer-events: none; } @@ -187,12 +190,20 @@ 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, @@ -345,14 +356,6 @@ 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; @@ -522,53 +525,7 @@ 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; @@ -611,7 +568,55 @@ input[type="color"]::-webkit-color-swatch-wrapper { height: 2px; } -#options select { +#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 { width: 100%; } @@ -636,6 +641,19 @@ 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; } @@ -674,7 +692,6 @@ 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; } @@ -707,6 +724,9 @@ 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; @@ -723,7 +743,7 @@ input[type="color"]::-webkit-color-swatch-wrapper { #toolsContent > .grid { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(4, 1fr); margin: 0.2em 0; } @@ -770,7 +790,7 @@ fieldset { text-overflow: ellipsis; } -.tabcontent li.buttonoff { +.tabcontent .buttonoff { background-color: var(--bg-disabled); color: #444444aa; } @@ -1248,6 +1268,7 @@ i.resetButton:active { padding: 0; height: 2px; background: #d4d4d4; + top: -0.35em; position: relative; appearance: none; -webkit-appearance: none; @@ -1512,6 +1533,20 @@ 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; } @@ -1809,6 +1844,11 @@ 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; @@ -1820,19 +1860,26 @@ div.editorLine { margin: 6px 0 0 6px; } -#unitsBody label { - display: inline-block; +#unitsBody > div > div { width: 9em; } +#unitsBody > div > input[type="range"] { + width: 7em; +} + #unitsBody > div > select, #unitsBody > div > input[type="text"] { - width: 14.4em; - border: 1px solid #e9e9e9; + width: 12em; } -#unitsBody input[type="range"] { - width: 9em; +#unitsBody > div > input[type="number"] { + width: 4.35em; +} + +#unitsBody > div > input, +#unitsBody > div > select { + border: 1px solid #e9e9e9; } #unitsEditor i.icon-lock-open, @@ -2367,34 +2414,6 @@ 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 5a18e579..6453976a 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" /> - + - + - +