diff --git a/app/index.html b/app/index.html
index c813133..656fe37 100644
--- a/app/index.html
+++ b/app/index.html
@@ -1200,6 +1200,7 @@ body {
// - Otherwise: hide
if (!categoriesUse) return true;
if (page.file === defaultPage()) return true;
+ if (page.uncategorized) return true;
const variants = page.variants || [];
if (variants.includes(activeCategory)) return true;
const cat = categoriesByCode[activeCategory];
@@ -1602,7 +1603,7 @@ function fmtDatetime(dtStr) {
// Category filter
if (categoriesUse && activeCategory) {
- posts = posts.filter(function(e) { return e.category === activeCategory; });
+ posts = posts.filter(function(e) { return !e.category || e.category === activeCategory; });
}
// Field filter
@@ -2245,7 +2246,7 @@ function fmtDatetime(dtStr) {
? navData.find(p => p.file === currentPage)
: null;
categoriesList.forEach(cat => {
- const hasVariant = !page || !page.variants || page.variants.includes(cat.code);
+ const hasVariant = !page || page.uncategorized || !(page.variants && page.variants.length) || page.variants.includes(cat.code);
const hasMsg = !!cat.notfoundmessage;
if (hasVariant || hasMsg || cat.code === activeCategory) out.add(cat.code);
});
@@ -2272,7 +2273,7 @@ function fmtDatetime(dtStr) {
'data-code': cat.code
});
option.appendChild(document.createTextNode(primary));
- const hasVariant = !page || !page.variants || page.variants.includes(cat.code);
+ const hasVariant = !page || page.uncategorized || !(page.variants && page.variants.length) || page.variants.includes(cat.code);
if (!hasVariant && cat.notfoundmessage) {
option.appendChild(el('span', { className: 'secondary', textContent: cat.notfoundmessage }));
} else if (secondary) {
diff --git a/mdcms.py b/mdcms.py
index 7050c9e..b50ebb5 100644
--- a/mdcms.py
+++ b/mdcms.py
@@ -266,10 +266,14 @@ def build_page_nav(
}
if categories_use:
covered = {}
+ has_uncategorized = False
for code, record in variants.items():
- key = code if code is not None else default_code
- if key:
- covered[key] = record.get("title", "")
+ if code is None:
+ has_uncategorized = True
+ else:
+ covered[code] = record.get("title", "")
+ if has_uncategorized:
+ entry["uncategorized"] = True
entry["variants"] = sorted(covered.keys())
entry["titles"] = covered
out.append(entry)
@@ -313,6 +317,8 @@ def generate_nav_yml(sections: list, pages: list, categories_use: bool = False)
if p.get("section-id"):
lines.append(f" section-id: {p['section-id']}")
lines.append(f" sort: {p.get('sort', 100)}")
+ if categories_use and p.get("uncategorized"):
+ lines.append(" uncategorized: true")
if categories_use and p.get("variants"):
lines.append(f" variants: [{', '.join(p['variants'])}]")
if categories_use and p.get("titles"):
@@ -344,8 +350,7 @@ def generate_search_json(
"body": r.get("body", ""),
}
if categories_use:
- code = r.get("code")
- entry["category"] = code if code is not None else default_code
+ entry["category"] = r.get("code") # None → null = show in all categories
out.append(entry)
return json.dumps(out, indent=2, ensure_ascii=False)