Merge pull request #13 from kbenestad/claude/linux-install-docs-cwQsb

Claude/linux install docs cw qsb
This commit is contained in:
Kristian Benestad 2026-05-16 22:46:38 +07:00 committed by GitHub
commit 56c22f075d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 173 additions and 38 deletions

View file

@ -22,6 +22,7 @@
# ──────────────────────────────────
sitename: MD-CMS New Site
navigation: topbar # sidebar | topbar
theme: theme.yml # presentational config — edit theme.yml to customise colours, fonts, and layout
# homepage: pages/home.md # override the default landing page
@ -30,20 +31,13 @@ navigation: topbar # sidebar | topbar
# favicon: favicon.png
# footer: "© 2026 Your Name"
# ──────────────────────────────────
# Typography (optional)
# ──────────────────────────────────
# font-title: "Inter:700"
# font-body: Inter
# font-code: JetBrains Mono
# ──────────────────────────────────
# Layout (optional)
# ──────────────────────────────────
# main-width: 80em
# nav-width: 20em
# nav-position: left # left | right (sidebar mode)
# Typography and colours are configured in theme.yml, not here.
# ──────────────────────────────────
# Features (optional)
# ──────────────────────────────────

View file

@ -117,6 +117,11 @@
--font-body-weight: 400;
--main-width: 80em;
--nav-width: 20em;
--line-height-body: 1.7;
--colour-info: #2563EB;
--colour-warning: #D97706;
--colour-success: #16A34A;
--colour-error: #DC2626;
}
html { font-size: 16px; scroll-behavior: smooth; }
@ -126,7 +131,7 @@ body {
font-weight: var(--font-body-weight);
color: var(--font-colour);
background: var(--bg-main);
line-height: 1.7;
line-height: var(--line-height-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
transition: background-color 0.2s ease, color 0.2s ease;
@ -1152,6 +1157,53 @@ body {
applyTheme(current === 'dark' ? 'light' : 'dark');
}
function applyThemeYml(tc) {
if (!tc) return;
const root = document.documentElement;
const getOrCreateStyle = id => {
let s = document.getElementById(id);
if (!s) { s = document.createElement('style'); s.id = id; document.head.appendChild(s); }
return s;
};
let modeCss = '';
['light', 'dark'].forEach(mode => {
const m = tc[mode];
if (!m) return;
const vars = [];
if (m.accent) {
const rgb = hexToRgb(m.accent);
vars.push(`--accent: ${m.accent}`);
vars.push(`--accent-rgb: ${rgb}`);
vars.push(`--nav-active-bg: rgba(${rgb}, 0.10)`);
vars.push(`--nav-hover-bg: rgba(${rgb}, 0.05)`);
vars.push(`--table-header-bg: rgba(${rgb}, 0.08)`);
vars.push(`--link-colour: ${m.accent}`);
}
if (m.background) { vars.push(`--bg-main: ${m.background}`); vars.push(`--search-bg: ${m.background}`); }
if (m['nav-background']) vars.push(`--bg-nav: ${m['nav-background']}`);
if (m.text) { vars.push(`--font-colour: ${m.text}`); vars.push(`--code-font: ${m.text}`); }
if (m['text-muted']) vars.push(`--font-colour-muted: ${m['text-muted']}`);
if (vars.length) modeCss += `:root[data-theme="${mode}"] { ${vars.join('; ')}; }\n`;
});
if (modeCss) getOrCreateStyle('theme-overrides').textContent = modeCss;
if (tc['colours-semantic']) {
const sem = tc['colours-semantic'];
const semVars = [];
if (sem.info) semVars.push(`--colour-info: ${sem.info}`);
if (sem.warning) semVars.push(`--colour-warning: ${sem.warning}`);
if (sem.success) semVars.push(`--colour-success: ${sem.success}`);
if (sem.error) semVars.push(`--colour-error: ${sem.error}`);
if (semVars.length) getOrCreateStyle('theme-semantic').textContent = `:root { ${semVars.join('; ')}; }`;
}
if (tc['main-width']) root.style.setProperty('--main-width', tc['main-width']);
if (tc['nav-width']) root.style.setProperty('--nav-width', tc['nav-width']);
if (tc['line-height']) root.style.setProperty('--line-height-body', String(tc['line-height']));
if (tc['font-size']) document.documentElement.style.fontSize = `${tc['font-size'] * 16}px`;
}
function applyConfigTheme() {
const root = document.documentElement;
['light', 'dark'].forEach(mode => {
@ -1211,35 +1263,48 @@ body {
}
// ─── Fonts ────────────────────────────────────────────────
function loadFonts() {
const fonts = {};
['font-title', 'font-body', 'font-code'].forEach(key => {
const val = config[key];
if (!val) return;
const [name, weight] = val.split(':');
fonts[key] = { name: name.trim(), weight: weight ? weight.trim() : '400' };
});
const googleFonts = [];
Object.entries(fonts).forEach(([, { name, weight }]) => {
googleFonts.push(`${name.replace(/ /g, '+')}:wght@${weight}`);
});
function loadFonts(tc) {
function parseFont(spec) {
if (!spec) return null;
const parts = spec.split(':');
if (parts.length >= 3) return { provider: parts[0].trim(), name: parts.slice(1, -1).join(':').trim(), weight: parts[parts.length - 1].trim() };
if (parts.length === 2) return { provider: 'google', name: parts[0].trim(), weight: parts[1].trim() };
return { provider: 'google', name: parts[0].trim(), weight: '400' };
}
const src = tc || {};
const bodyFont = parseFont(src['font-body'] || config['font-body']);
const headingFont = parseFont(src['font-heading'] || src['font-title'] || config['font-title']);
const codeFont = parseFont(src['font-code'] || config['font-code']);
const allFonts = [bodyFont, headingFont, codeFont].filter(Boolean);
const bunnyFonts = allFonts.filter(f => f.provider === 'bunny');
const googleFonts = allFonts.filter(f => f.provider === 'google');
if (bunnyFonts.length) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = `https://fonts.bunny.net/css?family=${bunnyFonts.map(f => `${f.name.replace(/ /g, '+')}:${f.weight}`).join('&family=')}`;
document.head.appendChild(link);
}
if (googleFonts.length) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = `https://fonts.googleapis.com/css2?${googleFonts.map(f => `family=${f}`).join('&')}&display=swap`;
link.href = `https://fonts.googleapis.com/css2?${googleFonts.map(f => `family=${f.name.replace(/ /g, '+')}:wght@${f.weight}`).join('&')}&display=swap`;
document.head.appendChild(link);
}
const root = document.documentElement;
if (fonts['font-title']) {
root.style.setProperty('--font-title', `"${fonts['font-title'].name}", system-ui, sans-serif`);
root.style.setProperty('--font-title-weight', fonts['font-title'].weight);
if (headingFont) {
root.style.setProperty('--font-title', `"${headingFont.name}", system-ui, sans-serif`);
root.style.setProperty('--font-title-weight', headingFont.weight);
}
if (fonts['font-body']) {
root.style.setProperty('--font-body', `"${fonts['font-body'].name}", system-ui, sans-serif`);
root.style.setProperty('--font-body-weight', fonts['font-body'].weight);
if (bodyFont) {
root.style.setProperty('--font-body', `"${bodyFont.name}", system-ui, sans-serif`);
root.style.setProperty('--font-body-weight', bodyFont.weight);
}
if (fonts['font-code']) {
root.style.setProperty('--font-code', `"${fonts['font-code'].name}", monospace`);
if (codeFont) {
root.style.setProperty('--font-code', `"${codeFont.name}", monospace`);
}
}
@ -2418,14 +2483,23 @@ function fmtDatetime(dtStr) {
if (link) link.href = `assets/images/${config.logo}`;
}
loadFonts();
let themeConfig = {};
if (config.theme) {
try {
const themeResp = await fetch(config.theme);
if (themeResp.ok) themeConfig = jsyaml.load(await themeResp.text()) || {};
} catch (e) { /* fall back to hardcoded CSS defaults */ }
}
loadFonts(themeConfig);
initCategories();
const navMode = config.navigation || 'sidebar';
if (navMode === 'topbar') buildTopbar();
else buildSidebar();
applyConfigTheme();
if (config.theme) applyThemeYml(themeConfig);
else applyConfigTheme();
applyTheme(getInitialTheme());
// nav.yml — phase 2 expects `sections:` + `pages:` blocks; phase 1 flat

66
app/theme.yml Normal file
View file

@ -0,0 +1,66 @@
# mdcms theme — default
# Edit colours, fonts, and layout here. See docs for full reference.
# ──────────────────────────────────
# Colours
# ──────────────────────────────────
light:
accent: "#2563EB"
background: "#FFFFFF"
nav-background: "#F8FAFC"
text: "#1E293B"
text-muted: "#64748B"
dark:
accent: "#60A5FA"
background: "#0F172A"
nav-background: "#1E293B"
text: "#F1F5F9"
text-muted: "#94A3B8"
# ──────────────────────────────────
# Semantic colours
# Used by callout tags (info, warning, success, error).
# Choose values that work on both light and dark backgrounds.
# ──────────────────────────────────
colours-semantic:
info: "#2563EB"
warning: "#D97706"
success: "#16A34A"
error: "#DC2626"
# ──────────────────────────────────
# Callout defaults
# ──────────────────────────────────
callouts:
info:
icon: info
primary-colour: "#2563EB"
background-colour: "#2563EB"
warning:
icon: warning
primary-colour: "#D97706"
background-colour: "#D97706"
success:
icon: success
primary-colour: "#16A34A"
background-colour: "#16A34A"
error:
icon: error
primary-colour: "#DC2626"
background-colour: "#DC2626"
# ──────────────────────────────────
# Typography
# Format: "provider:Font Name:weight" (provider: bunny | google)
# ──────────────────────────────────
font-body: "bunny:Noto Sans:400"
font-heading: "bunny:Noto Sans:700"
font-size: 1.0 # unitless multiplier (1.0 = 16px base)
line-height: 1.7 # unitless multiplier
# ──────────────────────────────────
# Layout
# ──────────────────────────────────
main-width: 80em
nav-width: 20em

View file

@ -1,16 +1,17 @@
#!/usr/bin/env python3
#
# mdcms v0.3.2 — CLI companion
# mdcms v0.3.3 — CLI companion
#
# Copyright 2026 Kristian Benestad
# Apache License, Version 2.0 — https://www.apache.org/licenses/LICENSE-2.0
"""MD-CMS v0.3.2 — CLI tool for managing and building MD-CMS sites."""
"""MD-CMS v0.3.3 — CLI tool for managing and building MD-CMS sites."""
import json
import os
import re
import ssl
import time
import urllib.error
import urllib.request
from pathlib import Path
@ -20,7 +21,7 @@ import certifi
import click
import yaml
CLI_VERSION = "0.3.2"
CLI_VERSION = "0.3.3"
CLI_RELEASE_DATE = "16 May 2026"
MIN_SUPPORTED_VERSION = "0.3"
@ -470,7 +471,7 @@ def _version_callback(ctx, param, value):
if not value or ctx.resilient_parsing:
return
click.echo(f"mdcms v{CLI_VERSION} (released {CLI_RELEASE_DATE})")
url = f"https://raw.githubusercontent.com/kbenestad/mdcms/refs/heads/main/docs/banner/v{CLI_VERSION}.txt"
url = f"https://raw.githubusercontent.com/kbenestad/mdcms/refs/heads/main/docs/banner/v{CLI_VERSION}.txt?t={int(time.time())}"
try:
ssl_ctx = ssl.create_default_context(cafile=certifi.where())
req = urllib.request.Request(url, headers={"User-Agent": f"mdcms/{CLI_VERSION}"})

View file

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "mdcms"
version = "0.3.2"
version = "0.3.3"
description = "MD-CMS — Markdown-based CMS companion CLI"
readme = "README.md"
license = { text = "Apache-2.0" }