this repo has no description

docs: add site redesign design doc and implementation plan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+816
+97
docs/plans/2026-03-03-site-redesign-design.md
··· 1 + # jon.recoil.org Site Redesign 2 + 3 + **Date**: 2026-03-03 4 + **Status**: Approved 5 + 6 + ## Goal 7 + 8 + Full redesign of jon.recoil.org — new toolchain, new content structure, modernized look — built entirely on the mono repo's ecosystem with OxCaml only (5.2.0+ox). 9 + 10 + ## Motivation 11 + 12 + - Showcase mono's capabilities (x-ocaml, odoc extensions, scrollycode, etc.) 13 + - Modernize the look and feel (clean, minimal — overreacted.io / Hillel Wayne / recoil.org family) 14 + - Better content organization (blog, notebooks, projects, reference docs) 15 + - Replace the old odoc_notebook-based toolchain with x-ocaml WebComponent 16 + 17 + ## Architecture: Two odoc shells 18 + 19 + **Approach**: Create a second odoc shell plugin (`odoc-jon-shell`) alongside `odoc-docsite`. 20 + 21 + - **`odoc-jon-shell`** — minimal, content-first shell for blog, notebooks, and project pages. No sidebar, centered content (~700px), clean typography. 22 + - **`odoc-docsite`** — used as-is for the `/reference/` documentation hub with sidebar, search, full docs layout. 23 + - **Single build pipeline** — `dune build @doc` generates everything with different shells for different page sets. 24 + - **x-ocaml WebComponent** works in both shells, styled via shared CSS variables. 25 + 26 + ## OxCaml Only 27 + 28 + Single compiler switch: 5.2.0+ox. All code examples, interactive notebooks, and the x-ocaml worker compile with OxCaml. Standard OCaml is a subset, so most existing content should work unchanged. 29 + 30 + ## Site Structure 31 + 32 + ``` 33 + / → Landing page (brief intro + content links) 34 + /blog/ → Blog index (reverse chronological) 35 + /blog/2025/weeknotes-03/ → Individual posts 36 + /notebooks/ → Notebook index 37 + /notebooks/foundations/1/ → Interactive notebook chapters 38 + /notebooks/oxcaml/... → OxCaml parallelism tutorials 39 + /projects/ → Project showcase index 40 + /projects/x-ocaml/ → Individual project pages with live demos 41 + /reference/ → odoc-docsite documentation hub (sidebar, search) 42 + /feed.xml → RSS/Atom feed 43 + ``` 44 + 45 + ## Content in Mono 46 + 47 + All site content lives in a `site/` directory in the mono repo: 48 + 49 + ``` 50 + mono/ 51 + ├── site/ 52 + │ ├── blog/ 53 + │ │ ├── 2025/ ← migrated from jon.recoil.org-src 54 + │ │ └── 2026/ 55 + │ ├── notebooks/ 56 + │ │ ├── foundations/ ← 11 chapters, Foundations of CS 57 + │ │ └── oxcaml/ ← OxCaml tutorials 58 + │ ├── projects/ ← new project showcase pages 59 + │ ├── static/ ← images, assets 60 + │ ├── index.mld ← landing page 61 + │ └── dune ← odoc rules 62 + ├── odoc-jon-shell/ ← new minimal shell plugin 63 + └── deploy-site.sh ← updated build script 64 + ``` 65 + 66 + ## Audience 67 + 68 + Mixed, depending on section: 69 + - **Blog**: broader tech audience 70 + - **Notebooks**: students and learners 71 + - **Projects**: OCaml community 72 + - **Reference**: OCaml developers 73 + 74 + ## Design Aesthetic 75 + 76 + - Clean, minimal, content-first (overreacted.io / Hillel Wayne) 77 + - recoil.org family feel (like anil.recoil.org but with interactive content) 78 + - Dark mode via `prefers-color-scheme` 79 + - No JavaScript SPA for main shell (simple static pages) 80 + - Design details to be iterated after content migration 81 + 82 + ## Development Strategy 83 + 84 + - Work on a **long-lived branch** in mono 85 + - **Phase 1**: Migrate content, build with OxCaml, minimal shell 86 + - **Phase 2**: Iterate on shell design, typography, layout 87 + - **Phase 3**: Project showcase pages, polish, deploy 88 + 89 + ## Content Migration 90 + 91 + From `jon.recoil.org-src`: 92 + - 45 blog .mld files (2025-2026) 93 + - 11 Foundations of CS notebook chapters 94 + - 4 OxCaml tutorial files 95 + - Static assets (images) 96 + - RSS feed generation (adapt gen_atom.exe or replace) 97 + - Notes, drafts, reference index
+719
docs/plans/2026-03-03-site-redesign-plan.md
··· 1 + # jon.recoil.org Site Redesign — Implementation Plan 2 + 3 + > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. 4 + 5 + **Goal:** Migrate all jon.recoil.org content into the mono repo and build it with OxCaml-only, using x-ocaml and odoc extensions for interactive notebooks, with a new minimal odoc shell. 6 + 7 + **Architecture:** Two odoc shells — a new minimal `odoc-jon-shell` for blog/notebooks/projects, and existing `odoc-docsite` for reference docs. All content in `site/` directory, all code compiled with OxCaml (5.2.0+ox). Content migrated from old `{@ocamltop}` format to new `{@ocaml}` interactive extension format. 8 + 9 + **Tech Stack:** OCaml (OxCaml 5.2.0+ox), odoc, odoc-interactive-extension, x-ocaml WebComponent, js_top_worker, dune 3.20+, TyXML 10 + 11 + --- 12 + 13 + ## Content Format Migration Reference 14 + 15 + The old site (jon.recoil.org-src) uses `odoc_notebook`. The new site uses `odoc-interactive-extension` + `x-ocaml`. Key transformations: 16 + 17 + | Old Format | New Format | 18 + |---|---| 19 + | `{@ocamltop[# code ;; ]}` | `{@ocaml[code]}` | 20 + | `{@ocamltop autorun[# code ;; ]}` | `{@ocaml run-on=load[code]}` | 21 + | `{@ocaml hidden[code]}` | `{@ocaml hidden[code]}` (same) | 22 + | `@libs pkg1 pkg2` (frontmatter) | `@x-ocaml.requires pkg1,pkg2` (tag) | 23 + | `@switch oxcaml` (frontmatter) | Remove (OxCaml-only) | 24 + | `@published YYYY-MM-DD` | Preserve as odoc tag for RSS | 25 + | `@notanotebook` | Remove (all pages use shell) | 26 + | `# ` prompt prefixes in code | Remove | 27 + | `;;` terminators in toplevel | Remove (unless needed for multi-expression cells) | 28 + 29 + --- 30 + 31 + ### Task 1: Create long-lived branch 32 + 33 + **Files:** 34 + - Working directory: `~/workspace/mono` 35 + 36 + **Step 1: Create and switch to the branch** 37 + 38 + ```bash 39 + cd ~/workspace/mono 40 + git checkout -b jon-site-redesign 41 + ``` 42 + 43 + **Step 2: Commit design doc** 44 + 45 + The design doc at `docs/plans/2026-03-03-site-redesign-design.md` was already created. Stage and commit it. 46 + 47 + ```bash 48 + git add docs/plans/2026-03-03-site-redesign-design.md docs/plans/2026-03-03-site-redesign-plan.md 49 + git commit -m "docs: add site redesign design doc and implementation plan" 50 + ``` 51 + 52 + --- 53 + 54 + ### Task 2: Create site/ directory structure 55 + 56 + **Files:** 57 + - Create: `site/` directory tree 58 + - Create: `site/dune` (placeholder) 59 + - Create: `site/index.mld` (placeholder landing page) 60 + 61 + **Step 1: Create directory structure** 62 + 63 + ```bash 64 + cd ~/workspace/mono 65 + mkdir -p site/blog/2025 site/blog/2026 site/notebooks/foundations site/notebooks/oxcaml site/projects site/static 66 + ``` 67 + 68 + **Step 2: Create placeholder index.mld** 69 + 70 + Create `site/index.mld`: 71 + ``` 72 + {0 jon.recoil.org} 73 + 74 + Welcome to jon.recoil.org. This site is built with OxCaml. 75 + 76 + {1 Sections} 77 + 78 + {ul 79 + {- {{!page-blog}Blog}} 80 + {- {{!page-notebooks}Notebooks}} 81 + {- {{!page-projects}Projects}} 82 + } 83 + ``` 84 + 85 + **Step 3: Create placeholder dune file** 86 + 87 + Create `site/dune`: 88 + ```dune 89 + (documentation 90 + (package root)) 91 + ``` 92 + 93 + Note: The exact dune rules for site content will need investigation. odoc documentation pages are typically declared via `(documentation ...)` stanzas. We may need to use `(odoc ...)` rules or a custom driver. This will be refined in Task 7. 94 + 95 + **Step 4: Commit** 96 + 97 + ```bash 98 + git add site/ 99 + git commit -m "feat: add site/ directory structure with placeholders" 100 + ``` 101 + 102 + --- 103 + 104 + ### Task 3: Create odoc-jon-shell plugin skeleton 105 + 106 + **Files:** 107 + - Create: `odoc-jon-shell/dune-project` 108 + - Create: `odoc-jon-shell/src/dune` 109 + - Create: `odoc-jon-shell/src/odoc_jon_shell.ml` 110 + - Create: `odoc-jon-shell/src/odoc_jon_shell_css.ml` 111 + 112 + **Step 1: Create dune-project** 113 + 114 + Create `odoc-jon-shell/dune-project`: 115 + ```dune 116 + (lang dune 3.18) 117 + (using dune_site 0.1) 118 + (name odoc-jon-shell) 119 + (generate_opam_files true) 120 + 121 + (package 122 + (name odoc-jon-shell) 123 + (synopsis "Minimal content-first shell for jon.recoil.org") 124 + (depends 125 + (ocaml (>= 4.14)) 126 + odoc)) 127 + ``` 128 + 129 + **Step 2: Create src/dune** 130 + 131 + Create `odoc-jon-shell/src/dune`: 132 + ```dune 133 + (library 134 + (public_name odoc-jon-shell.impl) 135 + (name odoc_jon_shell) 136 + (libraries odoc.html odoc.extension_api)) 137 + 138 + (plugin 139 + (name odoc-jon-shell) 140 + (libraries odoc-jon-shell.impl) 141 + (site (odoc extensions))) 142 + ``` 143 + 144 + **Step 3: Create minimal CSS module** 145 + 146 + Create `odoc-jon-shell/src/odoc_jon_shell_css.ml`: 147 + 148 + ```ocaml 149 + let css = 150 + {| 151 + :root { 152 + --max-width: 700px; 153 + --body-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 154 + Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", 155 + sans-serif; 156 + --mono-font: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; 157 + --text-color: #1a1a1a; 158 + --bg-color: #ffffff; 159 + --link-color: #0969da; 160 + --muted-color: #656d76; 161 + --border-color: #d0d7de; 162 + --code-bg: #f6f8fa; 163 + } 164 + 165 + @media (prefers-color-scheme: dark) { 166 + :root { 167 + --text-color: #e6edf3; 168 + --bg-color: #0d1117; 169 + --link-color: #58a6ff; 170 + --muted-color: #8b949e; 171 + --border-color: #30363d; 172 + --code-bg: #161b22; 173 + } 174 + } 175 + 176 + * { box-sizing: border-box; margin: 0; padding: 0; } 177 + 178 + body { 179 + font-family: var(--body-font); 180 + font-size: 18px; 181 + line-height: 1.6; 182 + color: var(--text-color); 183 + background: var(--bg-color); 184 + } 185 + 186 + .jon-shell-header { 187 + max-width: var(--max-width); 188 + margin: 0 auto; 189 + padding: 1.5rem 1rem; 190 + display: flex; 191 + justify-content: space-between; 192 + align-items: baseline; 193 + } 194 + 195 + .jon-shell-header a { 196 + color: var(--text-color); 197 + text-decoration: none; 198 + font-weight: 600; 199 + } 200 + 201 + .jon-shell-header nav a { 202 + font-weight: 400; 203 + margin-left: 1.5rem; 204 + color: var(--muted-color); 205 + } 206 + 207 + .jon-shell-header nav a:hover { 208 + color: var(--text-color); 209 + } 210 + 211 + .jon-shell-main { 212 + max-width: var(--max-width); 213 + margin: 0 auto; 214 + padding: 0 1rem 4rem; 215 + } 216 + 217 + .jon-shell-main h1 { font-size: 2rem; margin: 2rem 0 1rem; } 218 + .jon-shell-main h2 { font-size: 1.4rem; margin: 1.8rem 0 0.8rem; } 219 + .jon-shell-main h3 { font-size: 1.15rem; margin: 1.5rem 0 0.6rem; } 220 + .jon-shell-main p { margin: 0.8rem 0; } 221 + 222 + .jon-shell-main a { 223 + color: var(--link-color); 224 + text-decoration: none; 225 + } 226 + .jon-shell-main a:hover { text-decoration: underline; } 227 + 228 + .jon-shell-main pre, .jon-shell-main code { 229 + font-family: var(--mono-font); 230 + font-size: 0.9em; 231 + } 232 + 233 + .jon-shell-main pre { 234 + background: var(--code-bg); 235 + padding: 1rem; 236 + border-radius: 6px; 237 + overflow-x: auto; 238 + margin: 1rem 0; 239 + } 240 + 241 + .jon-shell-main code { 242 + background: var(--code-bg); 243 + padding: 0.15em 0.3em; 244 + border-radius: 3px; 245 + } 246 + 247 + .jon-shell-main pre code { 248 + background: none; 249 + padding: 0; 250 + } 251 + 252 + .jon-shell-main img { 253 + max-width: 100%; 254 + height: auto; 255 + } 256 + 257 + .jon-shell-main ul, .jon-shell-main ol { 258 + margin: 0.8rem 0; 259 + padding-left: 1.5rem; 260 + } 261 + 262 + .jon-shell-main li { margin: 0.3rem 0; } 263 + 264 + .jon-shell-main blockquote { 265 + border-left: 3px solid var(--border-color); 266 + padding-left: 1rem; 267 + color: var(--muted-color); 268 + margin: 1rem 0; 269 + } 270 + 271 + .jon-shell-footer { 272 + max-width: var(--max-width); 273 + margin: 0 auto; 274 + padding: 2rem 1rem; 275 + border-top: 1px solid var(--border-color); 276 + color: var(--muted-color); 277 + font-size: 0.85rem; 278 + } 279 + |} 280 + ``` 281 + 282 + **Step 4: Create shell implementation** 283 + 284 + Create `odoc-jon-shell/src/odoc_jon_shell.ml`: 285 + 286 + Study `odoc-docsite/src/odoc_docsite_shell.ml` for the pattern, then implement a minimal version. The shell needs to: 287 + 288 + 1. Register CSS support file via `Odoc_extension_registry.register_support_file` 289 + 2. Implement `make` and `make_src` functions using TyXML 290 + 3. Register via `Html_shell.register` 291 + 292 + The page structure should be: 293 + ```html 294 + <!DOCTYPE html> 295 + <html> 296 + <head> 297 + <meta charset="utf-8"> 298 + <meta name="viewport" content="width=device-width, initial-scale=1"> 299 + <title>{page title}</title> 300 + <link rel="stylesheet" href="{path to jon-shell.css}"> 301 + {extension resources - CSS/JS for x-ocaml, etc.} 302 + </head> 303 + <body class="odoc jon-shell"> 304 + <header class="jon-shell-header"> 305 + <a href="/">jon.recoil.org</a> 306 + <nav> 307 + <a href="/blog/">blog</a> 308 + <a href="/notebooks/">notebooks</a> 309 + <a href="/projects/">projects</a> 310 + <a href="/reference/">reference</a> 311 + </nav> 312 + </header> 313 + <main class="jon-shell-main"> 314 + {odoc-rendered content} 315 + </main> 316 + <footer class="jon-shell-footer"> 317 + jon ludlam 318 + </footer> 319 + {extension resources - deferred JS} 320 + </body> 321 + </html> 322 + ``` 323 + 324 + Use `odoc_docsite_shell.ml` as reference for: 325 + - How to resolve resource URLs (CSS, JS paths) 326 + - How to inject extension resources (x-ocaml.js, etc.) 327 + - How to serialize with `Tyxml.Html` 328 + - How to handle `uses_katex` 329 + - How to produce `Odoc_document.Renderer.page` output 330 + 331 + **Step 5: Verify it compiles** 332 + 333 + ```bash 334 + cd ~/workspace/mono 335 + dune build odoc-jon-shell 336 + ``` 337 + 338 + **Step 6: Commit** 339 + 340 + ```bash 341 + git add odoc-jon-shell/ 342 + git commit -m "feat: add odoc-jon-shell plugin skeleton" 343 + ``` 344 + 345 + --- 346 + 347 + ### Task 4: Write content migration script 348 + 349 + **Files:** 350 + - Create: `scripts/migrate_content.sh` 351 + 352 + The old content format needs mechanical transformations. Write a script that: 353 + 354 + 1. Copies .mld files from `~/workspace/jon.recoil.org-src/blog/` to `~/workspace/mono/site/blog/` 355 + 2. Copies .mld files from `~/workspace/jon.recoil.org-src/notebooks/` to `~/workspace/mono/site/notebooks/` 356 + 3. Copies static assets from `~/workspace/jon.recoil.org-src/static/` to `~/workspace/mono/site/static/` 357 + 4. Applies these transformations to each .mld file: 358 + - Replace `{@ocamltop autorun[` with `{@ocaml run-on=load[` 359 + - Replace `{@ocamltop[` with `{@ocaml[` 360 + - Replace `@libs ` lines with `@x-ocaml.requires` format (space-separated → comma-separated) 361 + - Remove `@switch oxcaml` lines 362 + - Remove `@notanotebook` lines 363 + - Remove `# ` prompt prefixes at the start of lines inside code blocks (this is tricky — may need manual review) 364 + 365 + **Important**: The prompt removal (`# ` prefix) and `;;` handling is context-sensitive and may not be fully automatable. The script should do what it can, and flag files that need manual review (e.g., files containing `{@ocamltop`). 366 + 367 + **Step 1: Write the migration script** 368 + 369 + **Step 2: Run it on a dry-run basis first** 370 + 371 + ```bash 372 + bash scripts/migrate_content.sh --dry-run 373 + ``` 374 + 375 + **Step 3: Run migration** 376 + 377 + ```bash 378 + bash scripts/migrate_content.sh 379 + ``` 380 + 381 + **Step 4: Manually review flagged files** 382 + 383 + Check any files the script couldn't fully transform. Focus on: 384 + - Code blocks that had `# ` prompts — verify the code still makes sense 385 + - Multi-expression cells that relied on `;;` — these may need `;;` kept 386 + - `{x@ocaml[` deferred blocks — these are a custom format that needs manual handling 387 + 388 + **Step 5: Commit** 389 + 390 + ```bash 391 + git add site/ scripts/migrate_content.sh 392 + git commit -m "feat: migrate content from jon.recoil.org-src" 393 + ``` 394 + 395 + --- 396 + 397 + ### Task 5: Adapt blog content for new format 398 + 399 + **Files:** 400 + - Modify: `site/blog/**/*.mld` (post-migration manual fixes) 401 + 402 + After the migration script runs, go through blog posts and fix issues: 403 + 404 + **Step 1: Check which blog posts have interactive code** 405 + 406 + ```bash 407 + grep -r "@ocaml" site/blog/ --include="*.mld" -l 408 + ``` 409 + 410 + Most blog posts may be text-only (marked `@notanotebook` in the old system). These need minimal changes. 411 + 412 + **Step 2: For posts with interactive code, verify the code blocks parse correctly** 413 + 414 + Try building just the blog content: 415 + ```bash 416 + dune build @doc 2>&1 | grep -i error 417 + ``` 418 + 419 + Fix any parse errors in .mld files. 420 + 421 + **Step 3: Verify `@published` tags are preserved** 422 + 423 + ```bash 424 + grep -r "@published" site/blog/ --include="*.mld" 425 + ``` 426 + 427 + Ensure every blog post still has its publication date. 428 + 429 + **Step 4: Commit fixes** 430 + 431 + ```bash 432 + git add site/blog/ 433 + git commit -m "fix: adapt blog content for new odoc format" 434 + ``` 435 + 436 + --- 437 + 438 + ### Task 6: Adapt notebook content for new format 439 + 440 + **Files:** 441 + - Modify: `site/notebooks/**/*.mld` 442 + 443 + Notebooks are the most complex migration — they have heavy use of interactive cells. 444 + 445 + **Step 1: Review Foundations notebooks** 446 + 447 + Check each foundations .mld file for: 448 + - Code blocks that used `# ` prompts (these need the prompts stripped) 449 + - `autorun` keyword (should now be `run-on=load`) 450 + - Library requirements (`@libs` → `@x-ocaml.requires`) 451 + - Math expressions (`{m ...}`) — these should work as-is if KaTeX is available 452 + - SVG/MIME output blocks — these may need manual adaptation 453 + 454 + **Step 2: Review OxCaml notebooks** 455 + 456 + The OxCaml notebooks include `.md` (markdown) files. These need a different approach: 457 + - `local.mld` → should work with annotation changes 458 + - `.md` files → check if odoc supports markdown, or convert to .mld 459 + 460 + Note: odoc has an `odoc-md` package in the mono repo for markdown support. Investigate whether this can handle the OxCaml .md files, or whether they need manual conversion to .mld. 461 + 462 + **Step 3: Test notebook builds** 463 + 464 + ```bash 465 + dune build @doc 2>&1 | head -50 466 + ``` 467 + 468 + **Step 4: Commit** 469 + 470 + ```bash 471 + git add site/notebooks/ 472 + git commit -m "fix: adapt notebook content for new interactive extension format" 473 + ``` 474 + 475 + --- 476 + 477 + ### Task 7: Set up dune rules for site content 478 + 479 + **Files:** 480 + - Modify: `site/dune` 481 + - Possibly modify: `dune-project` (root) 482 + 483 + This is an investigation-heavy task. The key question: how should site content be built with odoc? 484 + 485 + **Step 1: Study how odoc-interactive-extension's demo pages are built** 486 + 487 + ```bash 488 + cat ~/workspace/mono/odoc-interactive-extension/doc/dune 489 + ``` 490 + 491 + This will show how .mld files with interactive content are configured for odoc builds. 492 + 493 + **Step 2: Decide on the dune approach** 494 + 495 + Options: 496 + - Use `(documentation ...)` stanza under the `root` package 497 + - Use a dedicated site package 498 + - Use `odoc-driver` directly (how the old site worked) 499 + 500 + **Step 3: Configure dune rules** 501 + 502 + Write the appropriate dune file for site content. The key requirement is that: 503 + - Site pages use `--shell jon-shell` (not docsite) 504 + - Reference pages continue using `--shell docsite` 505 + - x-ocaml runtime is available 506 + - jtw universe is available for interactive cells 507 + 508 + **Step 4: Test build** 509 + 510 + ```bash 511 + dune build @doc 512 + ``` 513 + 514 + **Step 5: Commit** 515 + 516 + ```bash 517 + git add site/dune dune-project 518 + git commit -m "feat: configure dune rules for site content" 519 + ``` 520 + 521 + --- 522 + 523 + ### Task 8: Update deploy-site.sh 524 + 525 + **Files:** 526 + - Modify: `deploy-site.sh` 527 + 528 + **Step 1: Read current deploy-site.sh** 529 + 530 + ```bash 531 + cat ~/workspace/mono/deploy-site.sh 532 + ``` 533 + 534 + **Step 2: Add site content build steps** 535 + 536 + The updated script needs to: 537 + 1. Build and install all packages (including odoc-jon-shell) — existing step 538 + 2. Build odoc docs for reference section — existing step, using `--shell docsite` 539 + 3. Build site content using `--shell jon-shell` — new step 540 + 4. Build jtw universe for OxCaml — existing step (but now OxCaml-only) 541 + 5. Assemble final output directory combining reference + site content 542 + 6. Optionally serve on localhost 543 + 544 + **Step 3: Test the full build** 545 + 546 + ```bash 547 + ./deploy-site.sh --no-serve 548 + ``` 549 + 550 + **Step 4: Verify output structure** 551 + 552 + ```bash 553 + ls -la _build/default/_doc/_html/ 554 + ``` 555 + 556 + Confirm the expected URL structure exists. 557 + 558 + **Step 5: Commit** 559 + 560 + ```bash 561 + git add deploy-site.sh 562 + git commit -m "feat: update deploy script for site content" 563 + ``` 564 + 565 + --- 566 + 567 + ### Task 9: Test full build with OxCaml 568 + 569 + **Files:** None (testing only) 570 + 571 + **Step 1: Verify OxCaml switch exists** 572 + 573 + ```bash 574 + opam switch list 575 + ``` 576 + 577 + If there's no OxCaml switch, this needs to be set up. The Dockerfile in jon.recoil.org-src shows how (opam switch create oxcaml 5.2.0+ox). 578 + 579 + **Step 2: Build everything with OxCaml switch** 580 + 581 + ```bash 582 + opam switch oxcaml # or whatever the switch is named 583 + eval $(opam env) 584 + ./deploy-site.sh --no-serve 585 + ``` 586 + 587 + **Step 3: Check for compilation errors** 588 + 589 + Any standard OCaml code that doesn't compile under OxCaml needs fixing. OxCaml is a superset, so this should be rare. 590 + 591 + **Step 4: Serve locally and smoke test** 592 + 593 + ```bash 594 + cd _build/default/_doc/_html && python3 -m http.server 8080 595 + ``` 596 + 597 + Open in browser, check: 598 + - Landing page renders 599 + - Blog posts render 600 + - Interactive notebooks work (cells execute) 601 + - Reference docs work (sidebar, search) 602 + - Dark mode works 603 + 604 + **Step 5: Fix any issues found** 605 + 606 + --- 607 + 608 + ### Task 10: Create landing page 609 + 610 + **Files:** 611 + - Modify: `site/index.mld` 612 + 613 + **Step 1: Write the landing page content** 614 + 615 + The landing page should have: 616 + - Brief intro (who, what) 617 + - Links to recent blog posts 618 + - Links to notebook series 619 + - Links to projects 620 + 621 + Use odoc markup. Keep it simple and content-forward. 622 + 623 + **Step 2: Build and verify** 624 + 625 + ```bash 626 + dune build @doc 627 + ``` 628 + 629 + Check output in browser. 630 + 631 + **Step 3: Commit** 632 + 633 + ```bash 634 + git add site/index.mld 635 + git commit -m "feat: add landing page content" 636 + ``` 637 + 638 + --- 639 + 640 + ### Task 11: Set up RSS/Atom feed generation 641 + 642 + **Files:** 643 + - Study: `~/workspace/jon.recoil.org-src/scripts/gen_atom.ml` 644 + - Create or modify: feed generation script in mono 645 + 646 + **Step 1: Understand old gen_atom.ml** 647 + 648 + Read `~/workspace/jon.recoil.org-src/scripts/gen_atom.ml`. It reads compiled .odoc files and extracts `@published` metadata. 649 + 650 + **Step 2: Adapt for new build** 651 + 652 + The approach depends on whether `@published` tags survive odoc compilation in the new system. Options: 653 + - If odoc preserves custom tags → adapt gen_atom.ml to read new .odoc output 654 + - If not → write a simpler script that reads .mld files directly and extracts `@published` from the text 655 + 656 + **Step 3: Generate feed** 657 + 658 + ```bash 659 + dune exec -- scripts/gen_atom.exe site/blog/ > _build/default/_doc/_html/feed.xml 660 + ``` 661 + 662 + **Step 4: Verify feed** 663 + 664 + Check that the feed is valid XML with correct entries. 665 + 666 + **Step 5: Integrate into deploy-site.sh** 667 + 668 + **Step 6: Commit** 669 + 670 + ```bash 671 + git add scripts/ deploy-site.sh 672 + git commit -m "feat: set up RSS feed generation" 673 + ``` 674 + 675 + --- 676 + 677 + ### Task 12: Create project showcase pages 678 + 679 + **Files:** 680 + - Create: `site/projects/index.mld` 681 + - Create: `site/projects/*.mld` (one per project) 682 + 683 + **Step 1: Identify projects to showcase** 684 + 685 + From the mono repo, the key projects are: 686 + - x-ocaml — interactive OCaml WebComponent 687 + - odoc extensions (interactive, scrollycode, mermaid, admonition, dot, MSC, RFC) 688 + - js_top_worker — web worker OCaml toplevel 689 + - merlin-js — in-browser type checking 690 + - odoc-docsite — documentation shell 691 + 692 + **Step 2: Write project pages** 693 + 694 + Each project page should have: 695 + - Brief description 696 + - Live demo (using x-ocaml interactive cells where applicable) 697 + - Link to reference docs (`/reference/package-name/`) 698 + - Link to source (GitHub/tangled.org) 699 + 700 + **Step 3: Build and verify** 701 + 702 + **Step 4: Commit** 703 + 704 + ```bash 705 + git add site/projects/ 706 + git commit -m "feat: add project showcase pages" 707 + ``` 708 + 709 + --- 710 + 711 + ## Execution Notes 712 + 713 + - **Tasks 1-3** are independent setup work and can proceed immediately 714 + - **Task 4** (migration script) is the critical path — everything after depends on having content in place 715 + - **Tasks 5-6** (content adaptation) may be iterative — expect to discover issues during builds 716 + - **Task 7** (dune rules) is the hardest technical uncertainty — how odoc builds site content separate from package docs 717 + - **Tasks 10-12** (landing page, RSS, projects) are content work that can happen in parallel once the build works 718 + - The **OxCaml-only constraint** simplifies the build significantly (one switch, one worker) 719 + - The **custom shell** can start minimal and be refined iteratively — the user explicitly wants content-first, design-later