mirror of
https://github.com/kbenestad/mdcms.git
synced 2026-06-18 15:24:32 +00:00
Merge branch 'main' of https://github.com/kbenestad/mdcms
This commit is contained in:
commit
03adf65c62
2 changed files with 31 additions and 19 deletions
|
|
@ -15,6 +15,10 @@ Every merge into `main` is a release. Before committing any change to `mdcms.py`
|
||||||
|
|
||||||
In practice: check out `development`, do the work, push to `development`, PR `development` → `main` when ready to release.
|
In practice: check out `development`, do the work, push to `development`, PR `development` → `main` when ready to release.
|
||||||
|
|
||||||
|
## Unreleased changelog
|
||||||
|
|
||||||
|
`docs/unreleased.md` is a living document that tracks every fix or feature on `development` that has not yet been merged to `main`. Keep it current: whenever a change lands on `development`, add or update an entry in `unreleased.md` in the same commit (or a follow-up commit to `development`). When a batch of changes is merged to `main` and released, clear the entries that were released and leave the file in place for the next round of work.
|
||||||
|
|
||||||
## What this project is
|
## What this project is
|
||||||
|
|
||||||
MD-CMS is a markdown-based static site system with two distinct parts:
|
MD-CMS is a markdown-based static site system with two distinct parts:
|
||||||
|
|
@ -166,6 +170,7 @@ For nested navigation, set `parent: <parent-section-code>` and `parent-sort` on
|
||||||
- Variant files: `<base>.<code>.md` — the suffix is only treated as a category if the code is declared in config
|
- Variant files: `<base>.<code>.md` — the suffix is only treated as a category if the code is declared in config
|
||||||
- `categories-sectionnames: per-category` requires each section in `nav.yml` to have a `categorynames` block with an entry per category code
|
- `categories-sectionnames: per-category` requires each section in `nav.yml` to have a `categorynames` block with an entry per category code
|
||||||
- RTL is set per category via `direction: rtl`
|
- RTL is set per category via `direction: rtl`
|
||||||
|
- Line height is set per category via `line-height: 2.8` (useful for scripts like Nastaliq that need extra vertical space). Restores to theme default when switching to a category without this key.
|
||||||
|
|
||||||
## Dynamic post tags (mdcms code blocks)
|
## Dynamic post tags (mdcms code blocks)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ if ('serviceWorker' in navigator) {
|
||||||
--link-visited-colour: #7C3AED;
|
--link-visited-colour: #7C3AED;
|
||||||
--link-decoration: underline;
|
--link-decoration: underline;
|
||||||
--link-hover-decoration: underline;
|
--link-hover-decoration: underline;
|
||||||
--link-hover-weight: 700;
|
--link-hover-weight: normal;
|
||||||
--link-visited-decoration: underline;
|
--link-visited-decoration: underline;
|
||||||
--search-bg: #FFFFFF;
|
--search-bg: #FFFFFF;
|
||||||
--search-border: #E2E8F0;
|
--search-border: #E2E8F0;
|
||||||
|
|
@ -113,7 +113,7 @@ if ('serviceWorker' in navigator) {
|
||||||
--link-visited-colour: #A78BFA;
|
--link-visited-colour: #A78BFA;
|
||||||
--link-decoration: underline;
|
--link-decoration: underline;
|
||||||
--link-hover-decoration: underline;
|
--link-hover-decoration: underline;
|
||||||
--link-hover-weight: 700;
|
--link-hover-weight: normal;
|
||||||
--link-visited-decoration: underline;
|
--link-visited-decoration: underline;
|
||||||
--search-bg: #1E293B;
|
--search-bg: #1E293B;
|
||||||
--search-border: #334155;
|
--search-border: #334155;
|
||||||
|
|
@ -171,7 +171,7 @@ body {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
transition: background-color 0.2s ease, transform 0.3s ease;
|
transition: background-color 0.2s ease, transform 0.3s ease, visibility 0s ease 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-sidebar.nav-left .sidebar { left: 0; }
|
.layout-sidebar.nav-left .sidebar { left: 0; }
|
||||||
|
|
@ -187,12 +187,13 @@ body {
|
||||||
.sidebar::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 2px; }
|
.sidebar::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 2px; }
|
||||||
.sidebar::-webkit-scrollbar-track { background: var(--scrollbar-track); }
|
.sidebar::-webkit-scrollbar-track { background: var(--scrollbar-track); }
|
||||||
|
|
||||||
|
.layout-sidebar .mobile-header { display: none; }
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════
|
/* ═══════════════════════════════════════════
|
||||||
SIDEBAR CONTENT
|
SIDEBAR CONTENT
|
||||||
═══════════════════════════════════════════ */
|
═══════════════════════════════════════════ */
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
padding: 1.5rem 1.25rem 1rem;
|
padding: 1.5rem 1.25rem 1rem;
|
||||||
border-bottom: 1px solid var(--divider);
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,7 +338,6 @@ body {
|
||||||
/* Sidebar footer */
|
/* Sidebar footer */
|
||||||
.sidebar-footer {
|
.sidebar-footer {
|
||||||
padding: 0.75rem 1.25rem;
|
padding: 0.75rem 1.25rem;
|
||||||
border-top: 1px solid var(--divider);
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -446,7 +446,7 @@ body {
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0.4rem;
|
padding: 0.4rem;
|
||||||
color: var(--font-colour);
|
color: var(--nav-link-colour, var(--font-colour));
|
||||||
z-index: 150;
|
z-index: 150;
|
||||||
}
|
}
|
||||||
.hamburger svg { width: 22px; height: 22px; }
|
.hamburger svg { width: 22px; height: 22px; }
|
||||||
|
|
@ -625,17 +625,17 @@ body {
|
||||||
padding: 0.55rem 0.85rem;
|
padding: 0.55rem 0.85rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.88rem;
|
font-size: 0.88rem;
|
||||||
color: var(--font-colour);
|
color: var(--nav-link-colour, var(--font-colour));
|
||||||
border-bottom: 1px solid var(--divider);
|
border-bottom: 1px solid var(--divider);
|
||||||
transition: background 0.1s;
|
transition: background 0.1s;
|
||||||
}
|
}
|
||||||
.category-option:last-child { border-bottom: none; }
|
.category-option:last-child { border-bottom: none; }
|
||||||
.category-option:hover { background: var(--nav-hover-bg); }
|
.category-option:hover { background: var(--nav-hover-bg); }
|
||||||
.category-option.active { background: var(--nav-active-bg); color: var(--accent); font-weight: 600; }
|
.category-option.active { background: var(--nav-active-bg); color: var(--nav-link-active-colour, var(--accent)); font-weight: 600; }
|
||||||
.category-option .secondary {
|
.category-option .secondary {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--font-colour-muted);
|
color: var(--nav-section-heading-colour, var(--font-colour-muted));
|
||||||
margin-top: 0.15rem;
|
margin-top: 0.15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -738,12 +738,13 @@ body {
|
||||||
|
|
||||||
.layout-sidebar .sidebar {
|
.layout-sidebar .sidebar {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
|
visibility: hidden;
|
||||||
width: 80vw;
|
width: 80vw;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
.layout-sidebar.nav-right .sidebar { transform: translateX(100%); }
|
.layout-sidebar.nav-right .sidebar { transform: translateX(100%); }
|
||||||
.layout-sidebar .sidebar.open { transform: translateX(0); }
|
.layout-sidebar .sidebar.open { transform: translateX(0); visibility: visible; transition: background-color 0.2s ease, transform 0.3s ease; }
|
||||||
.layout-sidebar .main-area { margin-left: 0 !important; margin-right: 0 !important; }
|
.layout-sidebar .main-area { margin-left: 0 !important; margin-right: 0 !important; }
|
||||||
.main-content { padding: 1rem 1.25rem 3rem; }
|
.main-content { padding: 1rem 1.25rem 3rem; }
|
||||||
|
|
||||||
|
|
@ -957,6 +958,7 @@ body {
|
||||||
let activeCategory = null; // current code
|
let activeCategory = null; // current code
|
||||||
let sectionnamesMode = 'same'; // 'same' | 'per-category'
|
let sectionnamesMode = 'same'; // 'same' | 'per-category'
|
||||||
let loadedFonts = new Set(); // track which font files have been loaded
|
let loadedFonts = new Set(); // track which font files have been loaded
|
||||||
|
let defaultLineHeight = null; // theme/CSS default, captured after theme is applied
|
||||||
|
|
||||||
// ─── Icons ────────────────────────────────────────────────
|
// ─── Icons ────────────────────────────────────────────────
|
||||||
const STANDARD_ICONS = ['dark_mode','light_mode','menu','search','arrow_right','arrow_drop_down','mobile_arrow_down','language','info','warning','error','success','exclamation','dangerous','report','history','text_compare','keyboard_arrow_right','keyboard_arrow_down','keyboard_double_arrow_right','keyboard_double_arrow_down','expand_content','collapse_content','add','minimize'];
|
const STANDARD_ICONS = ['dark_mode','light_mode','menu','search','arrow_right','arrow_drop_down','mobile_arrow_down','language','info','warning','error','success','exclamation','dangerous','report','history','text_compare','keyboard_arrow_right','keyboard_arrow_down','keyboard_double_arrow_right','keyboard_double_arrow_down','expand_content','collapse_content','add','minimize'];
|
||||||
|
|
@ -1091,9 +1093,16 @@ body {
|
||||||
|
|
||||||
async function maybeLoadCategoryFont(code) {
|
async function maybeLoadCategoryFont(code) {
|
||||||
const cat = categoriesByCode[code];
|
const cat = categoriesByCode[code];
|
||||||
if (!cat || !cat.font || loadedFonts.has(cat.font)) return;
|
if (!cat || !cat.font) {
|
||||||
showFontLoadingBanner();
|
document.body.style.fontFamily = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
const family = 'mdcms-cat-' + code;
|
const family = 'mdcms-cat-' + code;
|
||||||
|
if (loadedFonts.has(cat.font)) {
|
||||||
|
document.body.style.fontFamily = `"${family}", ${getComputedStyle(document.documentElement).getPropertyValue('--font-body').trim()}`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showFontLoadingBanner();
|
||||||
const css = `@font-face { font-family: "${family}"; src: url("assets/fonts/${cat.font}"); }`;
|
const css = `@font-face { font-family: "${family}"; src: url("assets/fonts/${cat.font}"); }`;
|
||||||
const style = document.createElement('style');
|
const style = document.createElement('style');
|
||||||
style.textContent = css;
|
style.textContent = css;
|
||||||
|
|
@ -1103,8 +1112,7 @@ body {
|
||||||
await face.load();
|
await face.load();
|
||||||
document.fonts.add(face);
|
document.fonts.add(face);
|
||||||
loadedFonts.add(cat.font);
|
loadedFonts.add(cat.font);
|
||||||
// Apply font to body for this session
|
document.body.style.fontFamily = `"${family}", ${getComputedStyle(document.documentElement).getPropertyValue('--font-body').trim()}`;
|
||||||
document.body.style.fontFamily = `"${family}", ${getComputedStyle(document.body).fontFamily}`;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Font load failed:', e);
|
console.warn('Font load failed:', e);
|
||||||
}
|
}
|
||||||
|
|
@ -2018,7 +2026,6 @@ function fmtDatetime(dtStr) {
|
||||||
const layout = el('div', { className: `layout-sidebar nav-${navPos}` });
|
const layout = el('div', { className: `layout-sidebar nav-${navPos}` });
|
||||||
|
|
||||||
const mobileHeader = el('div', { className: 'mobile-header' });
|
const mobileHeader = el('div', { className: 'mobile-header' });
|
||||||
mobileHeader.style.display = 'none';
|
|
||||||
const hamburger = el('button', { className: 'hamburger', 'aria-label': 'Open menu' });
|
const hamburger = el('button', { className: 'hamburger', 'aria-label': 'Open menu' });
|
||||||
hamburger.appendChild(iconEl('menu'));
|
hamburger.appendChild(iconEl('menu'));
|
||||||
const mobileName = el('span', { className: 'sidebar-sitename', textContent: config.sitename || 'MD-CMS' });
|
const mobileName = el('span', { className: 'sidebar-sitename', textContent: config.sitename || 'MD-CMS' });
|
||||||
|
|
@ -2028,10 +2035,6 @@ function fmtDatetime(dtStr) {
|
||||||
}
|
}
|
||||||
mobileHeader.appendChild(mobileName);
|
mobileHeader.appendChild(mobileName);
|
||||||
|
|
||||||
const mobileStyle = document.createElement('style');
|
|
||||||
mobileStyle.textContent = `@media (max-width: 768px) { .layout-sidebar .mobile-header { display: flex !important; } }`;
|
|
||||||
document.head.appendChild(mobileStyle);
|
|
||||||
|
|
||||||
const overlay = el('div', { className: 'mobile-overlay' });
|
const overlay = el('div', { className: 'mobile-overlay' });
|
||||||
overlay.addEventListener('click', () => closeMobileMenu());
|
overlay.addEventListener('click', () => closeMobileMenu());
|
||||||
|
|
||||||
|
|
@ -2298,6 +2301,9 @@ function fmtDatetime(dtStr) {
|
||||||
document.querySelectorAll('.category-bar').forEach(b => b.setAttribute('dir', direction));
|
document.querySelectorAll('.category-bar').forEach(b => b.setAttribute('dir', direction));
|
||||||
document.querySelectorAll('.md-content, .title-bar').forEach(el => el.setAttribute('dir', direction));
|
document.querySelectorAll('.md-content, .title-bar').forEach(el => el.setAttribute('dir', direction));
|
||||||
document.querySelectorAll('.sidebar').forEach(el => el.setAttribute('dir', direction));
|
document.querySelectorAll('.sidebar').forEach(el => el.setAttribute('dir', direction));
|
||||||
|
// Apply per-category line-height override, or restore theme default
|
||||||
|
const lh = cat && cat['line-height'];
|
||||||
|
document.documentElement.style.setProperty('--line-height-body', lh ? String(lh) : (defaultLineHeight || '1.7'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function doSearch(query, resultsEl) {
|
function doSearch(query, resultsEl) {
|
||||||
|
|
@ -2770,6 +2776,7 @@ function fmtDatetime(dtStr) {
|
||||||
|
|
||||||
if (config.theme) applyThemeYml(themeConfig);
|
if (config.theme) applyThemeYml(themeConfig);
|
||||||
else applyConfigTheme();
|
else applyConfigTheme();
|
||||||
|
defaultLineHeight = getComputedStyle(document.documentElement).getPropertyValue('--line-height-body').trim() || '1.7';
|
||||||
applyTheme(getInitialTheme());
|
applyTheme(getInitialTheme());
|
||||||
|
|
||||||
// nav.yml — phase 2 expects `sections:` + `pages:` blocks; phase 1 flat
|
// nav.yml — phase 2 expects `sections:` + `pages:` blocks; phase 1 flat
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue