diff --git a/public/modules/dynamic/editors/states-editor.js b/public/modules/dynamic/editors/states-editor.js
index dbd645c55..e5a8efc9c 100644
--- a/public/modules/dynamic/editors/states-editor.js
+++ b/public/modules/dynamic/editors/states-editor.js
@@ -192,10 +192,15 @@ function statesEditorAddLines() {
totalBurgs += s.burgs;
}
+ // Manual assignment picks the brush state by clicking its row, so every state must have a
+ // row in the DOM. Pagination would hide states beyond the first page (the pager is hidden in
+ // this mode too), making them unselectable. Render the full set while painting.
+ const isManualAssignment = customization === 2;
const pageInfo = getEditorPage(allStates, statesPage);
+ const rows = isManualAssignment ? allStates : pageInfo.items;
let lines = "";
- for (const s of pageInfo.items) {
+ for (const s of rows) {
const area = getArea(s.area);
const rural = s.rural * populationRate;
const urban = s.urban * populationRate * urbanization;
@@ -309,10 +314,15 @@ function statesEditorAddLines() {
ensureEl("statesFooterPopulation").innerHTML = si(totalPopulation);
ensureEl("statesFooterPopulation").dataset.population = totalPopulation;
- renderEditorPagination(ensureEl("statesFooter"), pageInfo, page => {
- statesPage.page = page;
- statesEditorAddLines();
- });
+ // No pager while painting: all rows are rendered and the footer is hidden anyway.
+ if (isManualAssignment) {
+ ensureEl("statesFooter").querySelector(":scope > .editorPagination")?.remove();
+ } else {
+ renderEditorPagination(ensureEl("statesFooter"), pageInfo, page => {
+ statesPage.page = page;
+ statesEditorAddLines();
+ });
+ }
// add listeners
$body.querySelectorAll(":scope > div").forEach($line => {
@@ -1350,6 +1360,9 @@ function exitAddStateMode() {
function openStateMergeDialog() {
const emblem = i => /* html */ ``;
const validStates = pack.states.filter(s => s.i && !s.removed);
+ // Mirror the editor's active sort so the merge list reads in the same order the user is
+ // looking at (e.g. by culture), instead of always by state id.
+ sortDataByActiveHeader(ensureEl("statesHeader"), validStates, STATES_SORT_ACCESSORS);
const statesSelector = validStates
.map(
@@ -1493,7 +1506,9 @@ function openStateMergeDialog() {
const burg = state.capital || 0;
const center = burg ? pack.burgs[burg].cell : state.center;
const name = state.name;
- const formName = "Province";
+ // Keep the former state's government form (e.g. a demoted Duchy stays " Duchy"),
+ // falling back to a generic "Province" if the state had no form name.
+ const formName = state.formName || "Province";
const fullName = name + " " + formName;
const color = state.color;
const coa = state.coa; // reuse the former state's emblem
@@ -1571,10 +1586,16 @@ function openStateMergeDialog() {
debug.selectAll(".highlight").remove();
States.getPoles();
- if (mergeToProvinces) Provinces.getPoles();
layerIsOn("toggleStates") ? drawStates() : toggleStates();
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
- layerIsOn("toggleProvinces") && drawProvinces();
+ // When demoting to provinces, force the provinces layer on so the newly created province is
+ // actually visible. Otherwise (layer off) the result looks identical to a plain merge.
+ if (mergeToProvinces) {
+ Provinces.getPoles();
+ layerIsOn("toggleProvinces") ? drawProvinces() : toggleProvinces();
+ } else if (layerIsOn("toggleProvinces")) {
+ drawProvinces();
+ }
drawStateLabels([rulingStateId]);
refreshStatesEditor();
diff --git a/public/modules/ui/editors.js b/public/modules/ui/editors.js
index ba3593cc0..9ec4c937e 100644
--- a/public/modules/ui/editors.js
+++ b/public/modules/ui/editors.js
@@ -1079,7 +1079,7 @@ function refreshAllEditors() {
// dynamically loaded editors
async function editStates() {
if (customization) return;
- const Editor = await import("../dynamic/editors/states-editor.js?v=1.122.14");
+ const Editor = await import("../dynamic/editors/states-editor.js?v=1.122.15");
Editor.open();
}