diff --git a/web/templates/index.html b/web/templates/index.html
index 1c2dd24..b99276c 100644
--- a/web/templates/index.html
+++ b/web/templates/index.html
@@ -474,9 +474,17 @@ let TAB_CONFIG = {
}
};
-let ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG)
- .flatMap(t => t.categories)
- .map(c => ciStr(c));
+// Categories belonging to any real tab (everything except "other").
+// "other" is defined as "whatever isn't in this set" — it must never
+// contribute to it, or its own contents would exclude themselves.
+function computeAssignedCategories() {
+ return Object.entries(TAB_CONFIG)
+ .filter(([key]) => key !== 'other')
+ .flatMap(([, t]) => t._allCategories || t.categories)
+ .map(c => ciStr(c));
+}
+
+let ASSIGNED_CATEGORIES = computeAssignedCategories();
// State
let configUUID = '{{.ConfigUUID}}';
@@ -822,19 +830,16 @@ async function loadCategoriesFromAPI() {
categoryOrderMap[ciStr(cat.code)] = cat.display_order;
});
- // Build list of unassigned categories
- const knownCodes = Object.values(TAB_CONFIG)
- .flatMap(t => t.categories)
- .map(c => ciStr(c));
+ // Build list of unassigned categories (excludes "other" itself, so
+ // this stays correct even if it's re-run after "other" was already
+ // populated by a previous call).
+ const knownCodes = computeAssignedCategories();
const unassignedCategories = cats
.filter(cat => !knownCodes.includes(ciStr(cat.code)))
.sort((a, b) => a.display_order - b.display_order)
.map(cat => cat.code);
- // Rebuild ASSIGNED_CATEGORIES from known (non-"other") tabs before
- // populating "other", so unassigned categories aren't immediately
- // excluded from the "other" tab by their own assignment.
ASSIGNED_CATEGORIES = knownCodes;
// Update "other" tab with unassigned categories
@@ -886,7 +891,7 @@ function applyServerSettings(settings) {
// before restoring .other — otherwise categories already parked in
// .other (e.g. by loadCategoriesFromAPI) get counted as "assigned"
// and immediately excluded from the "other" tab again.
- ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG).flatMap(t => t.categories).map(c => ciStr(c));
+ ASSIGNED_CATEGORIES = computeAssignedCategories();
TAB_CONFIG.other = otherTab || { categories: [], singleSelect: false, label: 'Other' };
}
@@ -1398,11 +1403,10 @@ function applyConfigTypeToTabs() {
}
});
- // Rebuild assigned categories index using the full static list (_allCategories),
- // not the filtered one — hidden categories still belong to their tab, not to Other.
- ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG)
- .flatMap(t => t._allCategories || t.categories)
- .map(c => ciStr(c));
+ // Rebuild assigned categories index using the full static list
+ // (_allCategories), not the filtered one — hidden categories still
+ // belong to their tab, not to Other.
+ ASSIGNED_CATEGORIES = computeAssignedCategories();
}
function updateTabVisibility() {