Skip to content

Commit 8093464

Browse files
testac974claude
andcommitted
Add preface to website table of contents
- Add FrontMatterItem type for standalone front matter items - Update build-book-structure to include preface in BookData - Add preface link to book-nav component before parts - Create /book/preface page route - Fix CI to use npm run build (triggers prebuild hook) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent d4c2f4c commit 8093464

9 files changed

Lines changed: 564 additions & 365 deletions

File tree

website/.github/workflows/deploy-pages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
run: npm ci
4242

4343
- name: Build
44-
run: npx next build
44+
run: npm run build
4545
env:
4646
NEXT_PUBLIC_BASE_PATH: ""
4747

website/app/book/preface/page.tsx

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { MermaidDiagram } from "@/components/mermaid-diagram"
2+
import { ChevronRight } from "lucide-react"
3+
import Link from "next/link"
4+
import { Button } from "@/components/ui/button"
5+
import { getFlatSections } from "@/lib/book-data-server"
6+
import { parseMarkdownFile, extractMermaidDiagrams } from "@/lib/markdown"
7+
import { notFound } from "next/navigation"
8+
import path from "path"
9+
10+
export default async function PrefacePage() {
11+
// Get the flat section list for navigation to first chapter
12+
const flat = getFlatSections()
13+
const next = flat.length > 0 ? flat[0] : null
14+
15+
// Load and parse markdown
16+
let markdownData
17+
try {
18+
const filePath = path.join(process.cwd(), '..', 'book', 'preface.md')
19+
markdownData = await parseMarkdownFile(filePath)
20+
} catch (error) {
21+
console.error('Error loading preface:', error)
22+
notFound()
23+
}
24+
25+
const { frontmatter, content, htmlContent } = markdownData
26+
27+
// Extract mermaid diagrams
28+
const mermaidDiagrams = extractMermaidDiagrams(content)
29+
30+
return (
31+
<article className="py-12 px-6 lg:px-12 max-w-3xl">
32+
{/* Breadcrumb */}
33+
<div className="flex items-center gap-2 text-sm text-muted-foreground mb-8">
34+
<Link href="/" className="hover:text-foreground transition-colors">
35+
Home
36+
</Link>
37+
<span>/</span>
38+
<span>Preface</span>
39+
</div>
40+
41+
{/* Header */}
42+
<header className="mb-12">
43+
<h1 className="text-3xl sm:text-4xl font-bold tracking-tight mb-4 text-balance">
44+
{frontmatter.title || 'Preface'}
45+
</h1>
46+
{frontmatter.abstract && (
47+
<p className="text-lg text-muted-foreground">
48+
{frontmatter.abstract}
49+
</p>
50+
)}
51+
</header>
52+
53+
{/* Content */}
54+
<div
55+
className="prose prose-invert prose-green max-w-none"
56+
dangerouslySetInnerHTML={{ __html: htmlContent }}
57+
/>
58+
59+
{/* Mermaid diagrams */}
60+
{mermaidDiagrams.map((diagram, index) => (
61+
<div key={index} className="my-8">
62+
<MermaidDiagram
63+
chart={diagram}
64+
caption={`Figure ${index + 1}`}
65+
/>
66+
</div>
67+
))}
68+
69+
{/* Navigation */}
70+
<nav className="mt-16 pt-8 border-t border-border flex items-center justify-between">
71+
<Button variant="ghost" disabled className="text-muted-foreground">
72+
Previous
73+
</Button>
74+
{next ? (
75+
<Button variant="ghost" asChild>
76+
<Link href={next.href} className="flex items-center gap-2">
77+
<span className="hidden sm:inline">Start Reading: {next.sectionTitle}</span>
78+
<span className="sm:hidden">Next</span>
79+
<ChevronRight className="h-4 w-4" />
80+
</Link>
81+
</Button>
82+
) : (
83+
<Button variant="ghost" disabled className="text-muted-foreground">
84+
Next
85+
<ChevronRight className="h-4 w-4 ml-2" />
86+
</Button>
87+
)}
88+
</nav>
89+
</article>
90+
)
91+
}
92+
93+
export async function generateMetadata() {
94+
try {
95+
const filePath = path.join(process.cwd(), '..', 'book', 'preface.md')
96+
const { frontmatter } = await parseMarkdownFile(filePath)
97+
98+
return {
99+
title: `${frontmatter.title || 'Preface'} | Agentic Coding Playbook`,
100+
description: frontmatter.abstract || 'Preface to the Agentic Coding Playbook',
101+
}
102+
} catch (error) {
103+
return {
104+
title: 'Preface | Agentic Coding Playbook',
105+
}
106+
}
107+
}

website/components/book-nav.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { usePathname } from "next/navigation"
55
import { ChevronDown, ChevronRight } from "lucide-react"
66
import { useState, useEffect } from "react"
77
import { cn } from "@/lib/utils"
8-
import { bookStructure } from "@/lib/book-data"
8+
import { bookStructure, frontMatter } from "@/lib/book-data"
99

1010
export function BookNav({ onNavigate }: { onNavigate?: () => void }) {
1111
const pathname = usePathname()
@@ -58,6 +58,28 @@ export function BookNav({ onNavigate }: { onNavigate?: () => void }) {
5858
Table of Contents
5959
</p>
6060

61+
{/* Front matter (Preface, etc.) */}
62+
{frontMatter.map((item) => {
63+
const href = `/book/${item.slug}`
64+
const isActive = pathname === href
65+
return (
66+
<Link
67+
key={item.slug}
68+
href={href}
69+
onClick={onNavigate}
70+
className={cn(
71+
"block py-1.5 px-2 rounded-md transition-colors font-medium",
72+
isActive
73+
? "bg-primary/10 text-primary"
74+
: "text-foreground hover:bg-muted",
75+
)}
76+
aria-current={isActive ? "page" : undefined}
77+
>
78+
{item.title}
79+
</Link>
80+
)
81+
})}
82+
6183
{bookStructure.map((part, partIndex) => {
6284
const isPartAvailable = part.available
6385
return (

website/lib/book-data-server.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
import 'server-only'
22
import { buildBookStructure, getFlatSections as getFlatSectionsFromParts } from './build-book-structure'
3-
import type { Part, Chapter, Section } from './book-types'
3+
import type { Part, Chapter, Section, BookData, FrontMatterItem } from './book-types'
44

55
/**
66
* Server-side function to get book structure
77
* This uses fs and can only be called from server components
88
*/
9-
export function getBookStructure(): Part[] {
9+
export function getBookStructure(): BookData {
1010
try {
1111
return buildBookStructure()
1212
} catch (error) {
1313
console.error('Error building book structure:', error)
1414
// Fallback to minimal structure
15-
return [
16-
{
17-
title: "Foundations",
18-
slug: "foundations",
19-
available: true,
20-
chapters: [],
21-
},
22-
]
15+
return {
16+
frontMatter: [],
17+
parts: [
18+
{
19+
title: "Foundations",
20+
slug: "foundations",
21+
available: true,
22+
chapters: [],
23+
},
24+
],
25+
}
2326
}
2427
}
2528

@@ -37,7 +40,7 @@ export type FlatSection = {
3740

3841
export function getFlatSections(): FlatSection[] {
3942
const structure = getBookStructure()
40-
return getFlatSectionsFromParts(structure).map(section => ({
43+
return getFlatSectionsFromParts(structure.parts).map(section => ({
4144
partTitle: section.partTitle,
4245
chapterTitle: section.chapterTitle,
4346
sectionTitle: section.sectionTitle,
@@ -46,3 +49,11 @@ export function getFlatSections(): FlatSection[] {
4649
filePath: section.filePath,
4750
}))
4851
}
52+
53+
/**
54+
* Server-side function to get front matter items
55+
*/
56+
export function getFrontMatter(): FrontMatterItem[] {
57+
const structure = getBookStructure()
58+
return structure.frontMatter
59+
}

website/lib/book-data.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import bookStructureData from './book-structure.json'
2-
import type { Part, Section, Chapter, FlatSection } from './book-types'
2+
import type { Part, Section, Chapter, FlatSection, FrontMatterItem, BookData } from './book-types'
33

44
/**
55
* Book structure loaded from pre-generated JSON
66
* This can be safely used in both client and server components
77
*/
8-
export const bookStructure: Part[] = bookStructureData as Part[]
8+
const data = bookStructureData as BookData
99

10-
export type { Part, Section, Chapter, FlatSection }
10+
export const bookStructure: Part[] = data.parts
11+
export const frontMatter: FrontMatterItem[] = data.frontMatter
12+
13+
export type { Part, Section, Chapter, FlatSection, FrontMatterItem, BookData }
1114

1215
/**
1316
* Build a flat list of all navigable sections with prev/next links

0 commit comments

Comments
 (0)