Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .markdownlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": "markdownlint/style",
"MD001": false,
"MD022": false,
"MD024": false,
"MD025": false,
"MD032": false,
"MD034": false,
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Marp-based slides for a CodeHub meetup talk on home networking and Pi-hole.

## Requirements

- Node.js 20 or newer
- Node.js 20.19 or newer
- pnpm 11 or newer

## Local development
Expand All @@ -16,7 +16,9 @@ pnpm install
pnpm dev
```

Marp serves the deck at a local URL. Open that URL in your browser to present or review the deck.
`pnpm dev` builds `dist/index.html` (including Mermaid transformation), serves `dist/`, and rebuilds when `slides.md` changes.

By default it uses port `8080`; if `8080` is busy it automatically uses the next available port and prints the URL.

## Build and export

Expand Down
3 changes: 3 additions & 0 deletions marp.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
module.exports = {
// html is required: slides use <br/> tags inside Mermaid diagram labels.
// Implication: raw HTML in slides.md will be rendered as-is in the output.
// Keep slides.md under source control and avoid user-supplied content.
html: true,
markdown: {
html: true,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"version": "0.1.0",
"packageManager": "pnpm@11.3.0",
"engines": {
"node": ">=20.0.0"
"node": ">=20.19.0"
},
"scripts": {
"dev": "pnpm exec marp slides.md --server --allow-local-files",
"dev": "node scripts/dev.mjs",
"build": "node scripts/build.mjs",
"serve": "http-server dist -p 8001 -o",
"preview": "pnpm build && pnpm serve",
Expand Down
4 changes: 2 additions & 2 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
allowBuilds:
'@biomejs/biome': set this to true or false
onlyBuiltDependencies:
- '@biomejs/biome'
6 changes: 4 additions & 2 deletions scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ async function main() {
// The deck starts without images. Copy assets only when they exist.
}

await runMarpBuild();
await runMarpBuild();

// Inject Mermaid.js into the generated HTML
execFileSync('node', [path.join(__dirname, 'inject-mermaid.mjs')], {
cwd: distDir,
stdio: 'inherit',
});}
});
}

main().catch((error) => {
console.error(error);
Expand Down
100 changes: 100 additions & 0 deletions scripts/dev.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { execFileSync, spawn } from 'node:child_process';
import net from 'node:net';
import { watchFile } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = process.cwd();
const slidesFile = path.join(rootDir, 'slides.md');
const buildScript = path.join(__dirname, 'build.mjs');

let isBuilding = false;
let rebuildPending = false;

function isPortAvailable(port) {
return new Promise((resolve) => {
const tester = net.createServer();

tester.once('error', () => resolve(false));
tester.once('listening', () => {
tester.close(() => resolve(true));
});

tester.listen(port, '0.0.0.0');
});
}

async function findAvailablePort(startPort, maxAttempts = 20) {
for (let index = 0; index < maxAttempts; index += 1) {
const candidate = startPort + index;
if (await isPortAvailable(candidate)) {
return candidate;
}
}

throw new Error(`No available port found from ${startPort} to ${startPort + maxAttempts - 1}.`);
}

function buildDeck() {
if (isBuilding) {
rebuildPending = true;
return;
}

isBuilding = true;

try {
execFileSync('node', [buildScript], {
cwd: rootDir,
stdio: 'inherit',
});
} finally {
isBuilding = false;

if (rebuildPending) {
rebuildPending = false;
buildDeck();
}
}
}

async function main() {
buildDeck();

const requestedPort = Number(process.env.PORT ?? '8080');
const port = await findAvailablePort(requestedPort);

if (port !== requestedPort) {
console.log(`[dev] Port ${requestedPort} is in use, using ${port} instead.`);
}

const server = spawn('pnpm', ['exec', 'http-server', 'dist', '-p', String(port), '-c-1', '-o'], {
cwd: rootDir,
stdio: 'inherit',
});

watchFile(slidesFile, { interval: 500 }, (current, previous) => {
if (current.mtimeMs !== previous.mtimeMs) {
console.log('[dev] slides.md changed; rebuilding...');
buildDeck();
}
});

function shutdown(code = 0) {
server.kill('SIGTERM');
process.exit(code);
}

process.on('SIGINT', () => shutdown(0));
process.on('SIGTERM', () => shutdown(0));

server.on('exit', (code) => {
process.exit(code ?? 0);
});
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
57 changes: 24 additions & 33 deletions scripts/inject-mermaid.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,32 @@ for (const codeBlock of codeBlocks) {
}
}

// Get the updated HTML
let html = dom.serialize();

// Add Mermaid.js library before closing body tag if not already present
if (!html.includes("mermaid.min.js")) {
const mermaidScript = `<style>
section pre.mermaid {
margin: 0.2em auto;
}

section pre.mermaid svg {
display: block;
margin: 0 auto;
max-width: 100%;
max-height: 52vh;
width: auto;
height: auto;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"><\/script>
<script>
mermaid.initialize({
startOnLoad: true,
theme: "default",
flowchart: { useMaxWidth: true, nodeSpacing: 20, rankSpacing: 24 },
sequence: { actorMargin: 24, messageMargin: 14, diagramMarginY: 8 },
themeVariables: { fontSize: "14px" },
});
mermaid.contentLoaded();
<\/script>
</body>`;

html = html.replace("</body>", mermaidScript);
// Inject Mermaid library and initialisation using the DOM rather than string
// replacement, so the serialised output is always well-formed.
// Version is pinned to avoid unexpected upstream changes within v10.
const MERMAID_VERSION = "10.9.3";
const MERMAID_CDN = `https://cdn.jsdelivr.net/npm/mermaid@${MERMAID_VERSION}/dist/mermaid.min.js`;

if (!document.querySelector(`script[src*="mermaid"]`)) {
const loaderScript = document.createElement("script");
loaderScript.src = MERMAID_CDN;
document.body.appendChild(loaderScript);

const initScript = document.createElement("script");
initScript.textContent = [
'mermaid.initialize({',
' startOnLoad: true,',
' theme: "default",',
' flowchart: { useMaxWidth: true, nodeSpacing: 20, rankSpacing: 24 },',
' sequence: { actorMargin: 24, messageMargin: 14, diagramMarginY: 8 },',
' themeVariables: { fontSize: "14px" },',
'});',
'mermaid.contentLoaded();',
].join("\n");
document.body.appendChild(initScript);
}

const html = dom.serialize();
writeFileSync(outputFile, html, "utf-8");
console.log(
`✓ Transformed ${codeBlocks.length} mermaid code block(s) and injected library`,
Expand Down
56 changes: 35 additions & 21 deletions slides.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,17 @@ style: |
font-size: 0.82em;
}

section pre.mermaid {
margin: 0.2em auto;
}

section pre.mermaid svg {
max-height: 44vh;
display: block;
margin: 0 auto;
max-width: 100%;
max-height: 52vh;
width: auto;
height: auto;
}
Comment thread
stiffneckjim marked this conversation as resolved.
---

Expand Down Expand Up @@ -83,11 +92,19 @@ style: |

---

## 1. Welcome & Context (5 min)
## 1. Welcome & Context

- Brief introductions
- Why home networking matters
- Common pain points: ads, tracking, slow and insecure DNS
- A brief introduction
- Our sponsors
- Rolling Q&A

---

### Why home networking matters

---

### Common pain points: ads, tracking, slow and insecure DNS

---

Expand All @@ -113,15 +130,19 @@ graph LR

---

## 2. Understanding DNS Basics (10 min)
## 2. Understanding DNS Basics

- What is DNS and how it works
- The problem: unfiltered DNS queries

---

### The problem: unfiltered DNS queries

- Privacy and security implications

---

## Demo: Default setup
### Demo: Default setup

```mermaid
graph LR
Expand All @@ -141,7 +162,7 @@ graph LR

---

### 3. Introducing Pi-hole (10 min)
## 3. Introducing Pi-hole

- What is Pi-hole?
- How it works as a network-wide ad blocker
Expand All @@ -156,17 +177,9 @@ graph LR
D -->|Stats & Config| E["Blocklists"]
```

**Live demo steps:**

1. Show network layout and device configuration
2. Open Pi-hole admin dashboard
3. Inspect recent queries in real-time
4. Highlight blocked vs allowed domains
5. Review performance impact and statistics

---

### 4. Installation Walkthrough (15 min)
## 4. Installation Walkthrough

- Prerequisites and setup
- Step-by-step installation process
Expand All @@ -191,7 +204,7 @@ graph LR

---

### 5. Configuration & Customization (10 min)
## 5. Configuration & Customization

- Blocklists and whitelisting
- Dashboard overview
Expand All @@ -200,14 +213,15 @@ graph LR

---

### 6. Troubleshooting & Best Practices (5 min)
## 6. Troubleshooting & Best Practices

- Common issues and solutions
- Performance optimization
- Maintenance tips

---

## Key takeaways
## 7. Key takeaways

- **Visibility first**: Query logs show you what's happening on your network
- **DNS is powerful**: One DNS change affects all devices automatically
Expand Down