Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6bbb21a
feat: escaped markdown renderer for the help assistant
barrulus Sep 1, 2026
8832425
feat: help gateway API client
barrulus Sep 1, 2026
e18ed8a
chore: align help API comments with revised gateway contract
barrulus Sep 1, 2026
b3dba81
feat: help assistant declined-state and limits helpers
barrulus Sep 1, 2026
fed5caf
feat: help assistant dialog wired to the gateway
barrulus Sep 1, 2026
88c6274
feat: first-party assistant bubble replaces OpenWidget
barrulus Sep 1, 2026
ecda79a
fix: guard help-assistant dialog lifecycle and add bounded auto-retry
barrulus Sep 1, 2026
cf7329e
fix: harden help gateway client against malformed 200 responses
barrulus Sep 1, 2026
f41a129
fix: bubble accessibility/tooltip, dead OpenWidget CSS, stale e2e/tes…
barrulus Sep 1, 2026
4291667
fix: lift assistant bubble clear of the scale bar, compact unlisted-o…
barrulus Sep 2, 2026
e309513
fix: fixed help dialog width and footer spacing
barrulus Sep 2, 2026
90d9186
feat: community help links (GitHub, Discord, Reddit) in the assistant…
barrulus Sep 2, 2026
f91820f
fix: help links point GitHub at the wiki, add Patreon to the footer
barrulus Sep 2, 2026
0f7445e
feat: help gateway token storage
barrulus Sep 2, 2026
504fcb4
feat: bearer auth on gateway calls with 401 signed-out handling
barrulus Sep 2, 2026
b870fd4
feat: Discord sign-in plumbing — fragment token, auth row, signed-out…
barrulus Sep 2, 2026
b2cd0fa
fix: guard help sign-in token stash against fixation, refresh stamps
barrulus Sep 2, 2026
8386693
test: e2e coverage for help sign-in fragment token stash
barrulus Sep 2, 2026
48883a6
fix: let help dialog children wrap inside the fixed width
barrulus Sep 2, 2026
c57743d
fix: keep the sign-in row from breaking mid-phrase in the help footer
barrulus Sep 2, 2026
6075b91
feat: conversation id plumbing for the help gateway
barrulus Sep 2, 2026
462ebfd
feat: conversation memory in the help dialog with boundary divider
barrulus Sep 2, 2026
010206f
fix: close conversation-id leaks in the help assistant
barrulus Sep 2, 2026
ccdcc67
feat: feedback transport with proper bodyless-204 handling
barrulus Sep 2, 2026
ae984e7
feat: thumbs feedback on help answers
barrulus Sep 2, 2026
c91928c
fix: help widget guard bodyless-204 ask, resync auth on 401 feedback,…
barrulus Sep 2, 2026
28ac796
feat: annex states and provinces by clicking on the map
barrulus Sep 4, 2026
90063f2
fix: fit label slider ranges to the group font size
barrulus Sep 4, 2026
f67975d
feat: option to redraw viewport layers only after a zoom
barrulus Sep 4, 2026
61c156f
fix: validate the stored label and burg group registries
barrulus Sep 5, 2026
d98dcb8
fix: strip zoom auto-visibility display from migrated label group styles
barrulus Sep 5, 2026
294aa1f
fix: redraw states after a merge and drop the annex mode number
barrulus Sep 5, 2026
207cb6d
Merge remote-tracking branch 'upstream/fix/label-style-display-harves…
barrulus Sep 5, 2026
f348e2c
Merge remote-tracking branch 'upstream/fix/validate-persisted-options…
barrulus Sep 5, 2026
1c18c65
Merge remote-tracking branch 'upstream/fix/label-slider-ranges' into …
barrulus Sep 5, 2026
2557721
Merge remote-tracking branch 'upstream/feat/states-annex-by-click' in…
barrulus Sep 5, 2026
a46d95c
Merge remote-tracking branch 'pr/1599' into chore/merge-1.151.2
barrulus Sep 5, 2026
7390f95
chore: changelog, what's new and docs for 1.151.2
barrulus Sep 5, 2026
6f68a10
Merge remote-tracking branch 'upstream/help-assistant' into chore/mer…
barrulus Sep 5, 2026
131ca78
chore: changelog, what's new and option doc for the help assistant
barrulus Sep 5, 2026
4943c31
test: keep the budget-code cases formatted the same on every Biome re…
barrulus Sep 5, 2026
493718d
docs: describe the help assistant and retire the OpenWidget mentions
barrulus Sep 5, 2026
df18b17
Merge pull request #1804 from Azgaar/chore/merge-1.151.2
barrulus Sep 5, 2026
1ce6393
Merge upstream 1.151.2: help assistant, annex by click, redraw option…
barrulus Sep 5, 2026
cd07a22
fix: keep the npmDepsHash, the dependency set is unchanged
barrulus Sep 5, 2026
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
8 changes: 8 additions & 0 deletions docs/architecture/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ state**._ A controller does **not** hold pure static data, services, or serializ
- **App-level UI** — dialogs and widgets that are opened over the map but say nothing about it:
the About dialog (`app-info`). They have a controller's lifecycle but not a controller's
subject, so they live here and load with the shell.
- **Shared editor behaviour** — interaction helpers several editors call rather than copy:
dialog helpers, tooltips, the default map events (`viewbox-events`), and map modes such as
`annex-mode`, which the States and Provinces editors both drive with their own merge logic.

Widgets like `hierarchy-tree` and `minimap` may move to `components/` if they generalize.

Expand Down Expand Up @@ -401,6 +404,7 @@ classic needs it.
- Generates or simulates world data → `generators/`
- Serializes, saves, loads, or exports state → `services/io/`
- Manages browser/app lifecycle, a platform asset, or app preferences → `services/`
- Talks to the project's help gateway on the user's behalf → `services/help/`
- A constant list or template, no behavior → `data/`
- A helper that reads no ambient state and has ≥2 consumers → `utils/`
- A shared type / interface → `types/`
Expand Down Expand Up @@ -533,6 +537,10 @@ Static content: lookup tables, templates, tuning constants, reference lists.
- **IO is a service.** Save/load/export live in `src/services/io/`. Like controllers, each
service/io module exports a single named object (`Save`, `Load`, `ExportMap`, …) reached
through the `Services` registry (`Services.Save.saveMap(...)`).
- **The help gateway client is a service.** `src/services/help/` (api, auth, conversation) is the
only code that talks to ask.azgaarsfmg.com; `controllers/help-assistant.ts` is the UI over it. The
client keeps the Discord sign-in token in `localStorage` and only the server-issued conversation id
in `sessionStorage`, never conversation content.

## Lazy module registry

Expand Down
8 changes: 7 additions & 1 deletion docs/architecture/data_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,9 @@ unordered `AddedLabel[]`:

At runtime, Label Group styles are indexed in `style.labels.groups`, keyed by group id. Current `.map` files
serialize the complete global `style` object at data index 48. Pre-1.140 migration reconstructs it from the
legacy SVG group attributes. All label types can share a group without changing their rendering primitive:
legacy SVG group attributes; pre-1.140 zoom auto-visibility wrote `display: none` into the group's inline style,
which the migration strips, and an auto-update pass (1.151.2) strips it from styles records saved by
1.150-1.151.1, so a stored group style never carries `display`. All label types can share a group without changing their rendering primitive:
a label with `pathPoints` is rendered as a `<textPath>`, and any other label as a positioned `<text>`, which
the Label Editor lets the user switch for any label regardless of its type. The fallback groups are `states`,
`provinces`, the configured default Burg group, and `added` respectively.
Expand All @@ -341,6 +343,10 @@ Ordered Label Group policy is stored in `options.labels`:
- `showAll`: `boolean` - temporary override for per-group active state, zoom bounds, and layer dependencies
- `groups`: `LabelGroupOptions[]` - ordered group definitions

`options.labels` and the Burg group registry are seeded from `localStorage` for every new map, so both are
validated on read (`Labels.parseStoredOptions`, `Burgs.parseStoredGroups`): a stored value that would leave
nothing to draw, such as an empty `groups` list, falls back to the defaults instead of being reused.

Each `LabelGroupOptions` contains:

- `name`: `string` - globally unique logical group id
Expand Down
12 changes: 5 additions & 7 deletions docs/wiki/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ Check out [Dev board](https://github.com/users/Azgaar/projects/3/views/1?sumFiel

Current version of the Fantasy Map Generator is the latest `master` branch. You can download it here: https://github.com/Azgaar/Fantasy-Map-Generator/archive/refs/heads/master.zip. Also see [the wiki](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A#can-i-use-the-generator-offline).

# Releases

**1.151.2 - 2026-09-05**:

- Help assistant: ask questions about the Generator in the app, answered from the wiki and Discord knowledge by _[barrulus](https://github.com/barrulus)_ [1.151.2]
- Annex states and provinces by clicking on the map by _[barrulus](https://github.com/barrulus)_ [1.151.2]
- Option to redraw labels, icons and relief only after a zoom by _[barrulus](https://github.com/barrulus)_ [1.151.2]
- Fix burg label tiers hidden at every zoom on maps migrated from pre-1.140 versions by _[barrulus](https://github.com/barrulus)_ [1.151.2]

**1.151.1 - 2026-09-03**:

- Brushes stamp by distance travelled: smooth, gap-free painting at any refresh rate by _[barrulus](https://github.com/barrulus)_ [1.151.1]

# Releases

**[1.150.0](https://github.com/Azgaar/Fantasy-Map-Generator/archive/refs/tags/v1.150.0.zip) - 2026-09-05**:

- Journey Editor and new Journeys layer [1.150.0]
Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/Dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ Loaded on demand:
* [JSZip](https://github.com/Stuk/jszip) by Stuart Knightley, David Duponchel, Franz Buchinger and António Afonso — tile export
* [Dropbox SDK](https://github.com/dropbox/dropbox-sdk-js) — saving to and loading from Dropbox
* [TinyMCE](https://www.tiny.cloud) — the rich text editor in the Notes editor
* [OpenWidget](https://openwidget.com) — the optional in-app assistant
* Help gateway at [ask.azgaarsfmg.com](https://ask.azgaarsfmg.com) — answers the in-app help assistant; nothing is bundled, the app only calls it when a question is asked
16 changes: 12 additions & 4 deletions docs/wiki/Knowledge Base.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Please try to reproduce the issue on your own. If it's reproducible, please log

### The map performance is poor, how can I improve it?

Toggle off unnecessary layers. Be mindful of the Relief Icons layer in particular – it’s the most resource-demanding one. Open the Generator in a separate browser window, make it much smaller (about 900 x 560 pixels) and re-generate the map. Then, zoom in to see the map in detail. It will reduce the rending area and drastically improve the performance. When generating maps, set Points number to 10K. Points (cells) number highly affects performance. Toggle off map and element filters. Close all irrelevant browser tabs and applications. Use a leading edge browser (fresh versions on Chrome or Edge). Firefox is reported to be slower
Toggle off unnecessary layers. Be mindful of the Relief Icons layer in particular – it’s the most resource-demanding one. Open the Generator in a separate browser window, make it much smaller (about 900 x 560 pixels) and re-generate the map. Then, zoom in to see the map in detail. It will reduce the rending area and drastically improve the performance. When generating maps, set Points number to 10K. Points (cells) number highly affects performance. Toggle off map and element filters. Close all irrelevant browser tabs and applications. Use a leading edge browser (fresh versions on Chrome or Edge). Firefox is reported to be slower. Set 'Redraw on zoom' in the Options tab to 'After zoom' to redraw labels, icons and relief once per gesture instead of on every frame

### Who owns the maps created?

Expand Down Expand Up @@ -338,7 +338,7 @@ Go to Tools -> Units and change the '1 map pixel' distance scale. To change how

### How to merge provinces?

Open the Provinces Editor, filter the list by the state the provinces belong to and click on the 'Merge several provinces into one' button at the dialog bottom. Provinces of different states cannot be merged, reassign them in the States Editor first. Alternatively remove a province and repaint its territory to another one with the Paint brush
Open the Provinces Editor and click the 'Annex provinces' button (crown icon) at the dialog bottom. Click the province that absorbs the others on the map, then click the provinces to annex; hold Shift to keep annexing several. A confirmation lists what will be merged, so a mis-click can be cancelled. To pick from a list instead, filter by the state the provinces belong to and use the 'Merge several provinces into one' button. Provinces of different states cannot be merged, reassign them in the States Editor first. Alternatively remove a province and repaint its territory to another one with the Paint brush

### Can you colour in the relief icons?

Expand Down Expand Up @@ -530,12 +530,20 @@ Not really. The size mostly depends on the points number and cannot be changed f

### How to clean this chat dialog? How can I remove the chat history?

You can open the website cdn.openwidget.com, then Open the Dev Console (F12) and type `localStorage.clear();` there. It will remove the chat history
The assistant remembers the conversation for the current browser tab only, and the service forgets it two hours after the last question. Close the tab or open the Generator in a new one to start fresh; the assistant marks the boundary with a 'new conversation' line

### How can I toggle you off? How can I hide the Assistant?

To hide the Assistant, go to Options -> Generator settings and set `Azgaar assistant` to Hide

### The assistant says 'No questions left today'. What now?

Questions are budgeted per day to keep the shared service affordable. Anonymous use gets a small allowance; click 'Sign in with Discord for more' in the assistant panel for a larger one. The wiki and the Discord server hold the same knowledge the assistant answers from

### What does the assistant send, and where?

Only the question you type and a conversation id go to the project's help gateway at ask.azgaarsfmg.com; nothing from your map or browser is sent. Answers are drawn from the wiki and Discord knowledge. Signing in with Discord stores a token in this browser, and 'Sign out' removes it

### How to make a river?

There are two ways to create a new river. Open the Rivers Overview (Tools -> Rivers), click on the Add River button and then click on a map cell to spawn a river from it, the river will be created automatically. Another way is to draw a river manually with the 'Create a new river selecting river cells' button. The same automatic placement is available as Tools -> Add -> River (Shift + 4)
Expand All @@ -550,7 +558,7 @@ You can create a new Regiment using the Regiments Overview. In can open if you c

### How do I merge countries?

To merge states (countries) open the States Editor from Tools and click on the "Merge several states into one" button at the dialog bottom. Then select states you want to merge
To merge states (countries) open the States Editor from Tools and click the "Annex states" button (crown icon) at the dialog bottom. Click the state that annexes the others on the map, then click the states to annex; hold Shift to keep annexing several. A confirmation lists what will be removed, so a mis-click can be cancelled. To pick from a list instead, use the "Merge several states into one" button next to it and tick the states to merge

### How to start a war?

Expand Down
1 change: 1 addition & 0 deletions docs/wiki/Q&A.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The performance mainly depends on the number of visible elements and visible map
* Toggle off map and element filters.
* Close all irrelevant browser tabs and applications.
* Use a leading edge browser (fresh versions on Chrome or Edge). Firefox is reported to be slower.
* Set _Redraw on zoom_ in the Options tab to _After zoom_: labels, icons and relief are redrawn once per gesture instead of on every frame.

### Who owns the maps created?
You. The Generator is licensed under [MIT license](https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE) and derivative works such as maps are free of charge. You can sell them or make them available for free.
Expand Down
3 changes: 2 additions & 1 deletion docs/wiki/Quick-Start-Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@ Generator settings:
* _Transparency_: opacity of the control panes.
* _Autosave interval_: number of minutes the map should be auto-saved to browser memory. Set to `0` to disable the autosave.
* _Onload behavior_: define what should be done when Generator is opened: a new map generated or a previously saved map auto-opened.
* _Azgaar assistant_: show or hide the in-app chat assistant.
* _Azgaar assistant_: show or hide the help bubble in the bottom right corner, which answers questions about using the Generator.
* _Speaker voice_: select the voice used to speak burg and other names. Voice synthesis is provided by the browser.
* _Emblem shape_: defines shield shape used during emblems generation.
* _Zoom extent_: minimal and maximal zoom levels. Click on the button on the right to restore the default values.
* _Rendering_: set map rendering quality. Best quality can reduce the map performance.
* _Redraw on zoom_: when labels, icons and relief are redrawn during a zoom or pan. _After zoom_ redraws once per gesture, which is faster on big maps but makes new content appear all at once.
* _Language_: load Google Translate and select a language to translate the interface. Automatic translation can break some functionality — use the reset icon or refresh the page if it does.

There is also the _Restore default options_ button. It cancels all user changes and refreshes the page.
Expand Down
5 changes: 4 additions & 1 deletion docs/wiki/User-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ Slider that determines the width of the stroke.

The lower the value, the thinner the line will be, to the point of non-existence, and conversely, the higher the value, the thicker the line will be.

For label groups the slider range follows the group's font size: stroke width runs up to half of it and letter spacing from a tenth below zero to half above, so river and state labels both get a usable range. The number input still accepts any value.

### Common style controls (Font related)

#### Font size
Expand Down Expand Up @@ -512,11 +514,12 @@ Below the map settings, the Options tab holds settings that apply immediately an
* Theme color and Transparency: the color and opacity of the dialogs.
* Autosave interval: how often the map is saved to browser storage.
* Onload behavior: generate a new map or load the last saved one when the page opens.
* Azgaar assistant: show or hide the in-app chat assistant.
* Azgaar assistant: show or hide the help bubble in the bottom right corner, which answers questions about the Generator from the wiki and Discord knowledge.
* Speaker voice: the voice used by the speaker buttons next to names.
* Emblem shape: the default coat of arms shield shape.
* Zoom extent: minimal and maximal zoom levels, `[1, 20]` by default.
* Rendering: SVG shape-rendering mode, trading quality for speed.
* Redraw on zoom: when labels, icons and relief are redrawn during a zoom or pan. _After zoom_ redraws once per gesture: faster on big maps, but new content appears all at once.
* Language: the interface language.

The **Configure World** button opens a separate dialog where you set the map size relative to the world, its latitude and longitude shift on the globe, the temperature at the equator and both poles, and the global precipitation. The **Restore default options** button resets everything and reloads the page.
11 changes: 4 additions & 7 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,13 @@ protocol.registerSchemesAsPrivileged([

/**
* A .map file is shared like a document, and the app builds markup out of what is inside it, so the one
* directive that matters is `script-src`: no origin but the build itself may supply code, save for the
* Assistant widget the user opts into. The rest stays permissive, because maps embed data/blob images and
* fonts and the AI providers are fetched over https. `unsafe-eval` is required by the goods distribution
* formulas, which compile to `new Function`
* directive that matters is `script-src`: no external origin may supply code. The rest stays permissive,
* because maps embed data/blob images and fonts and the AI providers are fetched over https. `unsafe-eval`
* is required by the goods distribution formulas, which compile to `new Function`
*/
const ASSISTANT_ORIGINS = "https://*.openwidget.com";

const CSP = [
"default-src 'self' data: blob:",
`script-src 'self' 'unsafe-inline' 'unsafe-eval' ${ASSISTANT_ORIGINS}`,
`script-src 'self' 'unsafe-inline' 'unsafe-eval'`,
"style-src 'self' 'unsafe-inline' https:",
"font-src 'self' data: https:",
"img-src 'self' data: blob: https:",
Expand Down
126 changes: 115 additions & 11 deletions public/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2589,17 +2589,6 @@ svg.button {
}
}

#chat-widget-container {
user-select: none;
z-index: 99 !important;
}

#chat-widget-minimized {
animation: fadeIn 1s ease-in;
transform: scale(0.65);
opacity: var(--bg-opacity);
}

@keyframes fadeIn {
from {
opacity: 0;
Expand Down Expand Up @@ -2743,3 +2732,118 @@ body.tour-free-roam * {
gap: 0.5em;
margin: 0.8em 0 0.4em 0;
}

#helpAssistantBubble {
position: fixed;
right: 16px;
bottom: 64px; /* clear of the scale bar in the corner */
width: 44px;
height: 44px;
border-radius: 50%;
background: #35424d;
color: #fff;
font-size: 1.4em;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 2px 6px rgb(0 0 0 / 35%);
z-index: 99;
}

/* the dialog is fixed-width; undo the global .dialog > div { width: max-content } so
children wrap instead of overflowing */
#helpAssistant > div {
width: auto;
}

#helpAssistant .helpAssistantLinks {
display: flex;
gap: 1.2em;
justify-content: center;
margin-top: 0.6em;
padding-top: 0.5em;
border-top: 1px solid rgb(0 0 0 / 12%);
font-size: 0.92em;
}

#helpAssistant .helpAssistantUnlisted {
max-width: 20em;
}

#helpAssistant .helpAssistantLog {
max-height: 40vh;
min-height: 8em;
overflow-y: auto;
margin-bottom: 0.5em;
}

#helpAssistant .helpAssistantDivider {
text-align: center;
opacity: 0.55;
font-size: 0.85em;
margin: 0.5em 0;
}

#helpAssistant .helpAssistantNewChat {
display: block;
text-align: right;
font-size: 0.85em;
margin-bottom: 0.2em;
}

#helpAssistant .helpAssistantAsked {
font-style: italic;
opacity: 0.8;
}

#helpAssistant .helpAssistantFeedback {
display: flex;
gap: 0.3em;
justify-content: flex-end;
margin-top: 0.2em;
}

#helpAssistant .helpAssistantFeedback button {
background: none;
border: none;
cursor: pointer;
opacity: 0.4;
font-size: 1em;
padding: 0 0.15em;
}

#helpAssistant .helpAssistantFeedback button:hover {
opacity: 0.8;
}

#helpAssistant .helpAssistantFeedback button.selected {
opacity: 1;
}

#helpAssistant .helpAssistantFooter {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: 0.4em 0.8em;
width: 100%;
margin-top: 0.3em;
}

#helpAssistant .helpAssistantFooter #helpAssistantAuth {
flex: 1;
text-align: right;
font-size: 0.92em;
white-space: nowrap;
}

#helpAssistant .helpAssistantDisclosure {
font-size: 0.85em;
opacity: 0.7;
margin-top: 0.5em;
}

#helpAssistant textarea {
width: 100%;
box-sizing: border-box;
}
1 change: 0 additions & 1 deletion public/libs/openwidget.min.js

This file was deleted.

Loading
Loading