feature: Reorganize notes, and add section descriptions

This commit is contained in:
Keith Solomon
2026-01-25 12:23:41 -06:00
parent 5465665c26
commit 2992c17fd7
8 changed files with 85 additions and 26 deletions

View File

@@ -9,6 +9,7 @@ const CONTENT_DIR = path.join(ROOT, 'content');
const DIST_DIR = path.join(ROOT, 'dist'); const DIST_DIR = path.join(ROOT, 'dist');
const TEMPLATE_DIR = path.join(ROOT, 'templates'); const TEMPLATE_DIR = path.join(ROOT, 'templates');
const ASSETS_DIR = path.join(ROOT, 'assets'); const ASSETS_DIR = path.join(ROOT, 'assets');
const SECTIONS_FILE = path.join(TEMPLATE_DIR, 'sections.json');
const md = new MarkdownIt({ const md = new MarkdownIt({
html: true, html: true,
@@ -57,6 +58,25 @@ const cleanDir = (dir) => {
const stripMarkdown = (text) => text.replace(/[`*_>#\-]/g, '').replace(/\s+/g, ' ').trim(); const stripMarkdown = (text) => text.replace(/[`*_>#\-]/g, '').replace(/\s+/g, ' ').trim();
const loadSectionMeta = () => {
if (!fs.existsSync(SECTIONS_FILE)) return [];
const raw = fs.readFileSync(SECTIONS_FILE, 'utf8');
try {
const data = JSON.parse(raw);
if (Array.isArray(data)) return data;
if (data && typeof data === 'object') {
return Object.entries(data).map(([slug, meta]) => ({
slug,
name: meta?.name || slug,
description: meta?.description || '',
}));
}
} catch (err) {
console.warn('Invalid sections.json, skipping section descriptions.', err);
}
return [];
};
const loadNotes = () => { const loadNotes = () => {
const files = walkMarkdownFiles(CONTENT_DIR); const files = walkMarkdownFiles(CONTENT_DIR);
return files.map((filePath) => { return files.map((filePath) => {
@@ -131,19 +151,39 @@ const buildPages = () => {
cleanDir(DIST_DIR); cleanDir(DIST_DIR);
const notes = loadNotes(); const notes = loadNotes();
const sectionMeta = loadSectionMeta();
const sectionMetaMap = new Map(
sectionMeta.map((item) => {
const slug = slugify(item.slug || item.name || '');
return [slug, { ...item, slug }];
})
);
const sectionsMap = new Map(); const sectionsMap = new Map();
notes.forEach((note) => { notes.forEach((note) => {
if (!sectionsMap.has(note.sectionSlug)) { if (!sectionsMap.has(note.sectionSlug)) {
const meta = sectionMetaMap.get(note.sectionSlug);
sectionsMap.set(note.sectionSlug, { sectionsMap.set(note.sectionSlug, {
name: note.section, name: meta?.name || note.section,
slug: note.sectionSlug, slug: note.sectionSlug,
description: meta?.description || '',
notes: [], notes: [],
}); });
} }
sectionsMap.get(note.sectionSlug).notes.push(note); sectionsMap.get(note.sectionSlug).notes.push(note);
}); });
sectionMetaMap.forEach((meta) => {
if (!sectionsMap.has(meta.slug)) {
sectionsMap.set(meta.slug, {
name: meta.name || meta.slug,
slug: meta.slug,
description: meta.description || '',
notes: [],
});
}
});
const sections = Array.from(sectionsMap.values()).sort((a, b) => a.name.localeCompare(b.name)); const sections = Array.from(sectionsMap.values()).sort((a, b) => a.name.localeCompare(b.name));
sections.forEach((section) => section.notes.sort((a, b) => (a.nav - b.nav) || a.title.localeCompare(b.title))); sections.forEach((section) => section.notes.sort((a, b) => (a.nav - b.nav) || a.title.localeCompare(b.title)));
@@ -165,17 +205,17 @@ const buildPages = () => {
<a class="card" href="/${note.sectionSlug}/${note.slug}/"> <a class="card" href="/${note.sectionSlug}/${note.slug}/">
<div class="badge">${section.name}</div> <div class="badge">${section.name}</div>
<h3>${note.title}</h3> <h3>${note.title}</h3>
<p class="muted">${note.summary}</p>
<div class="tag-list">${note.tags.map((tag) => `<span class="tag">#${tag}</span>`).join('')}</div> <div class="tag-list">${note.tags.map((tag) => `<span class="tag">#${tag}</span>`).join('')}</div>
</a> </a>
`) `)
.join(''); .join('');
const sectionDescription = section.description || `Notes grouped by ${section.name}.`;
const sectionContent = ` const sectionContent = `
<div class="hero"> <div class="hero">
<p class="eyebrow">Section</p> <p class="eyebrow">Section</p>
<h1>${section.name}</h1> <h1>${section.name}</h1>
<p class="muted">Notes grouped by ${section.name}. Use the sidebar or search to jump in.</p> <p class="muted">${sectionDescription}</p>
</div> </div>
<div class="card-grid">${sectionList}</div> <div class="card-grid">${sectionList}</div>
`; `;
@@ -227,8 +267,7 @@ const buildPages = () => {
<a class="card" href="/${section.slug}/"> <a class="card" href="/${section.slug}/">
<div class="badge">${section.notes.length} note${section.notes.length === 1 ? '' : 's'}</div> <div class="badge">${section.notes.length} note${section.notes.length === 1 ? '' : 's'}</div>
<h3>${section.name}</h3> <h3>${section.name}</h3>
<p class="muted">${section.notes[0]?.summary || 'Section overview'} <p class="muted">${section.description || 'Section overview'}</p>
</p>
</a> </a>
`) `)
.join(''); .join('');
@@ -236,8 +275,8 @@ const buildPages = () => {
const homeContent = ` const homeContent = `
<div class="hero"> <div class="hero">
<p class="eyebrow">Workspace</p> <p class="eyebrow">Workspace</p>
<h1>Developer Notes Hub</h1> <h1>Development Notes Hub</h1>
<p class="muted">Markdown-first notes rendered into a fast static site. Use search or browse by section.</p> <p class="muted">Notes related to various aspects of my projects and homelab.</p>
</div> </div>
<div class="card-grid">${summaryCards}</div> <div class="card-grid">${summaryCards}</div>
`; `;

View File

@@ -1,13 +1,11 @@
--- ---
title: Cloudflare Pages Workflow title: Cloudflare Pages Workflow
section: infra section: dev-notes
summary: Steps to publish the static notes site to Cloudflare Pages using the provided workflow and build output. summary: Steps to publish the static notes site to Cloudflare Pages using the provided workflow and build output
tags: [cloudflare, ci, deploy] tags: [cloudflare, ci, deploy]
nav: 1 nav: 3
--- ---
# Cloudflare Pages Workflow
## Overview ## Overview
This note captures the build and deploy flow for the static notes site. The site compiles Markdown into static HTML under the `dist/` directory. This note captures the build and deploy flow for the static notes site. The site compiles Markdown into static HTML under the `dist/` directory.

View File

@@ -1,17 +1,16 @@
--- ---
title: Markdown Authoring Guide title: Markdown Authoring Guide
section: docs section: dev-notes
summary: Conventions for writing notes, front matter fields, and embedding code or media in the site. summary: Conventions for writing notes, front matter fields, and embedding code or media in the site
tags: [markdown, style, notes] tags: [markdown, style, notes]
nav: 1 nav: 2
--- ---
# Markdown Authoring Guide
## Front matter ## Front matter
Provide metadata at the top of every note: Provide metadata at the top of every note:
``` ```markdown
--- ---
title: Example Title title: Example Title
section: docs section: docs
@@ -25,13 +24,15 @@ nav: 1
- `nav`: optional integer to influence ordering within a section; lower numbers show first. - `nav`: optional integer to influence ordering within a section; lower numbers show first.
## Writing tips ## Writing tips
- Start with an `#` heading matching the title.
- Keep paragraphs short; use bullet lists for tasks. - Keep paragraphs short; use bullet lists for tasks.
- Use fenced code blocks with language hints (` ```bash `, ` ```js `) for highlighting. - Use fenced code blocks with language hints (` ```bash `, ` ```js `) for highlighting.
- Link to related notes with absolute paths, e.g., `/docs/markdown-authoring-guide/`. - Link to related notes with absolute paths, e.g., `/docs/markdown-authoring-guide/`.
## Media ## Media
Place images next to the note or in an `/assets/media` folder and reference relatively. Place images next to the note or in an `/assets/media` folder and reference relatively.
## Testing locally ## Testing locally
Run `npm run build` to regenerate HTML. Open `dist/index.html` in a browser to review layout and syntax highlighting. Run `npm run build` to regenerate HTML. Open `dist/index.html` in a browser to review layout and syntax highlighting.

View File

@@ -1,12 +1,12 @@
--- ---
title: Developer Onboarding title: Developer Onboarding
section: ops section: dev-notes
summary: Quick start steps to clone, install dependencies, and generate the site locally. summary: Quick start steps to clone, install dependencies, and generate the site locally
tags: [onboarding, setup] tags: [onboarding, setup]
nav: 1 nav: 1
--- ---
# Developer Onboarding ## Getting started
1. Clone the repository and install Node 20+. 1. Clone the repository and install Node 20+.
2. Run `npm install` to pull dependencies. 2. Run `npm install` to pull dependencies.
@@ -14,11 +14,14 @@ nav: 1
4. Serve `dist/` via any static server (e.g., `npx serve dist`). 4. Serve `dist/` via any static server (e.g., `npx serve dist`).
## Directory overview ## Directory overview
- `content/`: Markdown notes with front matter. - `content/`: Markdown notes with front matter.
- `templates/`: HTML shells for header, footer, and layout. - `templates/`: HTML shells for header, footer, and layout.
- `assets/`: CSS and JavaScript shared across pages. - `assets/`: CSS and JavaScript shared across pages.
## Conventions ## Conventions
- Use kebab-case filenames. - Use kebab-case filenames.
- For organization, use sections defined in `sections.json` and include section in filename.
- Keep summaries short; they populate listings and the search index. - Keep summaries short; they populate listings and the search index.
- Add tags to improve search results. - Add tags to improve search results.

View File

@@ -1,7 +1,7 @@
--- ---
title: Gateway Documentation title: Gateway Documentation
section: docs section: docs
summary: Homelab Gateway — Traefik Reverse Proxy, Authelia SSO, LAN Routing, macvlan IP assignment, and Docker-managed services. summary: Homelab Gateway — Traefik Reverse Proxy, Authelia SSO, LAN Routing, macvlan IP assignment, and Docker-managed services
tags: [networking, traefik, authelia, infrastructure, docker, home-lab] tags: [networking, traefik, authelia, infrastructure, docker, home-lab]
nav: 2 nav: 2
--- ---

View File

@@ -3,7 +3,7 @@ title: SoloForge Documentation
section: docs section: docs
summary: SoloForge Infrastructure Documentation for Self-Hosted Gitea with Actions Runner on Hetzner summary: SoloForge Infrastructure Documentation for Self-Hosted Gitea with Actions Runner on Hetzner
tags: [servers, infrasctructure, gitea, ci/cd, hetzner, docker] tags: [servers, infrasctructure, gitea, ci/cd, hetzner, docker]
nav: 1 nav: 2
--- ---
## Self-Hosted Gitea + Actions Runner (Hetzner Deployment) ## Self-Hosted Gitea + Actions Runner (Hetzner Deployment)

View File

@@ -1,7 +1,7 @@
--- ---
title: My Servers title: My Servers
section: network section: infra
summary: My Servers. summary: Documentation relating to the various servers I use and maintain
tags: [hardware, servers, infrasctructure] tags: [hardware, servers, infrasctructure]
nav: 1 nav: 1
--- ---

18
templates/sections.json Normal file
View File

@@ -0,0 +1,18 @@
[
{
"name": "Dev Notes",
"description": "Documentation relating to the Dev Notes project."
},
{
"name": "Docs",
"description": "Documentation relating to various aspects of my projects."
},
{
"name": "Infra",
"description": "Network and server documentation."
},
{
"name": "Other",
"description": "Miscellaneous stuff that doesn't fit in other categories."
}
]