SPB Git

spb/spboucher.ai Public

spboucher.ai — personal website of Simon-Pierre Boucher.

TypeScript 93.4% HTML 5.5% CSS 1%
5.5 KB · 173 lines typescript
Raw Blame History
1/*2  blog.ts3  spboucher.ai Web4  Author: Simon-Pierre Boucher5  Mail: contact@spboucher.ai6*/78import fs from "node:fs";9import path from "node:path";1011export interface BlogPostMeta {12  slug: string;13  file: string;14  title: string;15  excerpt: string;16  date: string; // ISO date17  dateLabel: string;18  tags: string[];19}2021export type BlogBlock =22  | { type: "h2"; text: string }23  | { type: "p"; text: string }24  | { type: "quote"; text: string }25  | { type: "ul"; items: string[] }26  | { type: "math"; tex: string };2728export interface BlogPost extends BlogPostMeta {29  blocks: BlogBlock[];30  readingMinutes: number;31}3233/** Registry of published essays — sources live in /blog/*.txt. */34const registry: BlogPostMeta[] = [35  {36    slug: "macroeconomics-of-white-collar-automation",37    file: "blog4.txt",38    title: "The Macroeconomics of White-Collar Automation",39    excerpt:40      "White-collar workers sit near the center of developed economies — their incomes support taxes, mortgages, consumption, and urban housing. If AI automates cognitive work faster than institutions redistribute the gains, the shock becomes macroeconomic, not merely occupational.",41    date: "2026-08-09",42    dateLabel: "August 9, 2026",43    tags: ["AI", "Economics", "Macroeconomics"],44  },45  {46    slug: "cost-of-intelligence",47    file: "blog1.txt",48    title: "What Happens When the Cost of Intelligence Approaches Zero?",49    excerpt:50      "For most of economic history, useful cognitive work required scarce, educated humans. AI breaks that coupling — and when the price of a fundamental input collapses, the entire economy reorganizes around whatever remains scarce.",51    date: "2026-08-09",52    dateLabel: "August 9, 2026",53    tags: ["AI", "Economics"],54  },55  {56    slug: "who-owns-ai-wealth",57    file: "blog2.txt",58    title: "If AI Creates Enormous Wealth, Who Owns It?",59    excerpt:60      "Growth and the distribution of growth are two different things. AI may create extraordinary wealth while shifting the defining economic divide from skilled versus unskilled labor to those who sell labor versus those who own productive intelligence.",61    date: "2026-08-09",62    dateLabel: "August 9, 2026",63    tags: ["AI", "Economics", "Ownership"],64  },65  {66    slug: "ai-prevents-its-own-singularity",67    file: "blog3.txt",68    title: "What If AI Prevents Its Own Singularity?",69    excerpt:70      "The intelligence explosion assumes fresh information. But as machines generate more of the internet they learn from, recursive contamination could turn the exponential into a plateau — unless AI becomes radically more empirical.",71    date: "2026-08-09",72    dateLabel: "August 9, 2026",73    tags: ["AI", "Data", "Scaling"],74  },75];7677/** Parse the constrained Markdown subset used by the essays. */78function parseBlocks(raw: string): BlogBlock[] {79  const lines = raw.split("\n");80  const blocks: BlogBlock[] = [];81  let listItems: string[] | null = null;82  let mathLines: string[] | null = null;8384  const flushList = () => {85    if (listItems && listItems.length > 0) {86      blocks.push({ type: "ul", items: listItems });87    }88    listItems = null;89  };9091  for (const rawLine of lines) {92    const line = rawLine.trim();9394    // Display math: $$ ... $$ on one line, or a fenced multi-line block.95    if (mathLines !== null) {96      if (line === "$$" || line.endsWith("$$")) {97        if (line !== "$$") mathLines.push(line.slice(0, -2).trim());98        blocks.push({ type: "math", tex: mathLines.join("\n").trim() });99        mathLines = null;100      } else {101        mathLines.push(line);102      }103      continue;104    }105    if (line.startsWith("$$")) {106      flushList();107      const inner = line.slice(2);108      if (inner.endsWith("$$") && inner.length >= 2) {109        blocks.push({ type: "math", tex: inner.slice(0, -2).trim() });110      } else {111        mathLines = inner.trim() ? [inner.trim()] : [];112      }113      continue;114    }115    if (line.length === 0) {116      flushList();117      continue;118    }119    if (line.startsWith("# ")) {120      // Post title — carried by the registry, not repeated in the body.121      flushList();122      continue;123    }124    if (line.startsWith("## ")) {125      flushList();126      blocks.push({ type: "h2", text: line.slice(3).trim() });127      continue;128    }129    if (line.startsWith("> ")) {130      flushList();131      blocks.push({ type: "quote", text: line.slice(2).trim() });132      continue;133    }134    if (line.startsWith("* ")) {135      (listItems ??= []).push(line.slice(2).trim().replace(/,$/, ""));136      continue;137    }138    flushList();139    blocks.push({ type: "p", text: line });140  }141  flushList();142  if (mathLines !== null && mathLines.length > 0) {143    blocks.push({ type: "math", tex: mathLines.join("\n").trim() });144  }145  return blocks;146}147148function loadPost(meta: BlogPostMeta): BlogPost {149  const raw = fs.readFileSync(150    path.join(process.cwd(), "blog", meta.file),151    "utf8",152  );153  const blocks = parseBlocks(raw);154  const words = raw.split(/\s+/).filter(Boolean).length;155  return { ...meta, blocks, readingMinutes: Math.max(1, Math.round(words / 220)) };156}157158/** All posts, newest first (registry order breaks ties). */159export function getBlogPosts(): BlogPost[] {160  return [...registry]161    .map(loadPost)162    .sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0));163}164165export function getBlogPost(slug: string): BlogPost | undefined {166  const meta = registry.find((p) => p.slug === slug);167  return meta ? loadPost(meta) : undefined;168}169170export function getBlogSlugs(): string[] {171  return registry.map((p) => p.slug);172}173