`)
+ .join(''),
+ });
+
+ const homePage = assemblePage({
+ pageTitle: 'Home',
+ header: renderHeader(null),
+ sidebar: homeSidebar,
+ content: homeContent,
+ });
+
+ writePage(path.join(DIST_DIR, 'index.html'), homePage);
+
+ // Assets and search index
+ copyAssets();
+ buildSearchIndex(notes);
+
+ console.log(`Built ${notes.length} notes across ${sections.length} sections → ${DIST_DIR}`);
+};
+
+buildPages();
diff --git a/content/docs-markdown-authoring.md b/content/docs-markdown-authoring.md
new file mode 100644
index 0000000..42d667c
--- /dev/null
+++ b/content/docs-markdown-authoring.md
@@ -0,0 +1,37 @@
+---
+title: Markdown Authoring Guide
+section: docs
+summary: Conventions for writing notes, front matter fields, and embedding code or media in the site.
+tags: [markdown, style, notes]
+nav: 1
+---
+
+# Markdown Authoring Guide
+
+## Front matter
+Provide metadata at the top of every note:
+
+```
+---
+title: Example Title
+section: docs
+summary: One-sentence overview for listings.
+tags: [topic, keyword]
+nav: 1
+---
+```
+
+- `section`: logical bucket (docs, infra, backend, frontend, etc.).
+- `nav`: optional integer to influence ordering within a section; lower numbers show first.
+
+## Writing tips
+- Start with an `#` heading matching the title.
+- Keep paragraphs short; use bullet lists for tasks.
+- Use fenced code blocks with language hints (` ```bash `, ` ```js `) for highlighting.
+- Link to related notes with absolute paths, e.g., `/docs/markdown-authoring-guide/`.
+
+## Media
+Place images next to the note or in an `/assets/media` folder and reference relatively.
+
+## Testing locally
+Run `npm run build` to regenerate HTML. Open `dist/index.html` in a browser to review layout and syntax highlighting.
diff --git a/content/infra-workflow.md b/content/infra-workflow.md
new file mode 100644
index 0000000..3bfbfda
--- /dev/null
+++ b/content/infra-workflow.md
@@ -0,0 +1,26 @@
+---
+title: Cloudflare Pages Workflow
+section: infra
+summary: Steps to publish the static notes site to Cloudflare Pages using the provided workflow and build output.
+tags: [cloudflare, ci, deploy]
+nav: 1
+---
+
+# Cloudflare Pages Workflow
+
+## 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.
+
+## Build steps
+1. Install dependencies: `npm install`.
+2. Generate the site: `npm run build`.
+3. Upload the `dist/` output to Cloudflare Pages.
+
+## Environment
+- Node 20+ is recommended.
+- CI uses `npm ci` and `npm run build` from the default branch.
+
+## Tips
+- Keep content in `content/` with front matter to expose metadata.
+- The build bundles a `search-index.json`; avoid storing secrets in front matter.
+- Regenerate locally before pushing to preview changes.
diff --git a/content/ops-onboarding.md b/content/ops-onboarding.md
new file mode 100644
index 0000000..414a5bc
--- /dev/null
+++ b/content/ops-onboarding.md
@@ -0,0 +1,24 @@
+---
+title: Developer Onboarding
+section: ops
+summary: Quick start steps to clone, install dependencies, and generate the site locally.
+tags: [onboarding, setup]
+nav: 1
+---
+
+# Developer Onboarding
+
+1. Clone the repository and install Node 20+.
+2. Run `npm install` to pull dependencies.
+3. Build with `npm run build`; output lands in `dist/`.
+4. Serve `dist/` via any static server (e.g., `npx serve dist`).
+
+## Directory overview
+- `content/`: Markdown notes with front matter.
+- `templates/`: HTML shells for header, footer, and layout.
+- `assets/`: CSS and JavaScript shared across pages.
+
+## Conventions
+- Use kebab-case filenames.
+- Keep summaries short; they populate listings and the search index.
+- Add tags to improve search results.
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..6a83b01
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,200 @@
+{
+ "name": "dev-notes-ssg",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "dev-notes-ssg",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "gray-matter": "^4.0.3",
+ "highlight.js": "^11.9.0",
+ "markdown-it": "^14.1.0"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gray-matter": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
+ "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-yaml": "^3.13.1",
+ "kind-of": "^6.0.2",
+ "section-matter": "^1.0.0",
+ "strip-bom-string": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "11.11.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
+ "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "license": "MIT",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/markdown-it/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "license": "MIT"
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/section-matter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
+ "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/strip-bom-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
+ "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "license": "MIT"
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..eadee2a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "dev-notes-ssg",
+ "version": "0.1.0",
+ "description": "Lightweight static site generator for personal dev notes",
+ "license": "MIT",
+ "scripts": {
+ "build": "node bin/build.js"
+ },
+ "dependencies": {
+ "gray-matter": "^4.0.3",
+ "highlight.js": "^11.9.0",
+ "markdown-it": "^14.1.0"
+ }
+}
diff --git a/prompt.txt b/prompt.txt
new file mode 100644
index 0000000..3858b61
--- /dev/null
+++ b/prompt.txt
@@ -0,0 +1,9 @@
+This repo will hold my personal collection of notes, etc for various dev projects. It should have a simple SSG that will render markdown (with appropriate front matter) into a sensible set of files that can be hosted via Cloudflare pages. I don't need something heavy like a standard SSG framework, even a simple php script that will render the content would be fine as long as it can do what I need. The goal is to have a simple, easy to maintain set of notes that I can quickly add to and update as needed. The content will be primarily markdown files with some front matter for metadata. The SSG should be able to parse the front matter and generate appropriate HTML files for hosting.
+
+Each page will have a standard structure with a header, footer, sidebar, and main content area, ideally separated into separate files. The header will include navigation links to each section of the notes (set in the front matter), while the footer will have links to my personal site and other content to be determined. The sidebar will contain links to each note in a section. The main content area will render the markdown content into HTML. Each section should have its own folder with an index page that lists all notes within that section, along with a brief description or excerpt from each note. The main index page should provide an overview of all sections and link to their respective index pages.
+
+For styling, I want a clean, minimalistic design that is easy to read and navigate. A dark theme with good contrast and legible fonts is preferred. Fonts used should be Raleway for headings and General Sans for body text (both from Google). Use a proper type scale utilizing fluid typography techniques based on a 20px main content text size. The layout should be responsive to ensure usability across different devices. Styling should follow a "utility-first" approach, similar to Tailwind CSS, to allow for easy customization and maintenance, but note I *DO NOT* want to use Tailwind itself. Colors should be customizable via CSS variables to allow for easy theming. Styling should be modular, with separate CSS files for layout, typography, components, and responsive adjustments.
+
+In the rendered HTML, code blocks should have syntax highlighting for better readability. I would also like to include support for images and other media within the notes, ensuring they are properly displayed in the rendered HTML. I also want to include a search functionality to quickly find notes based on keywords or tags. This can be a simple client-side search using JavaScript to filter through the notes.
+
+Finally, the SSG should be easy to run locally for testing and generating the static files before deployment to Cloudflare pages. A repo action to generate the static pages should be included.
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..81b11ba
--- /dev/null
+++ b/templates/base.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ {{pageTitle}} | Dev Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{extraHead}}
+
+
+
+