@@ -46,6 +46,9 @@ def build_all_pages():
4646 if page == "manual" :
4747 build_lab_manual ()
4848 continue
49+ elif page == "posts" :
50+ build_posts ()
51+ continue
4952
5053 content_file = content_dir / f"{ page } .yaml"
5154 content = yaml .safe_load (content_file .open ().read ())[page ]
@@ -81,6 +84,86 @@ def build_all_pages():
8184 shutil .copytree ("assets" , build_dir / "assets" )
8285
8386
87+ def build_posts ():
88+ """Build blog-post-like pages from markdown files in content/posts/."""
89+ posts_dir = content_dir / "posts"
90+ if not posts_dir .exists ():
91+ return
92+
93+ environment = Environment (loader = FileSystemLoader (template_dir ))
94+ template = environment .from_string ((template_dir / "post.html" ).open ().read ())
95+ md = Markdown (
96+ extras = [
97+ "fenced-code-blocks" ,
98+ "highlightjs-lang" ,
99+ "metadata" ,
100+ "tables" ,
101+ "footnotes" ,
102+ ]
103+ )
104+
105+ posts_metadata = [] # Collect metadata for index
106+
107+ for post_file in posts_dir .glob ("*.md" ):
108+ slug = post_file .stem
109+ raw_content = post_file .read_text ()
110+
111+ html_content = md .convert (raw_content )
112+ metadata = getattr (md , "metadata" , {}) or {}
113+
114+ page_dir = build_dir / "p" / slug
115+ page_dir .mkdir (exist_ok = True , parents = True )
116+ page_file = page_dir / "index.html"
117+
118+ page_url = f"/p/{ slug } /"
119+
120+ # Collect for index
121+ posts_metadata .append (
122+ {
123+ "slug" : slug ,
124+ "url" : page_url ,
125+ "title" : metadata .get ("title" , slug .replace ("-" , " " ).title ()),
126+ "subtitle" : metadata .get ("subtitle" ),
127+ "date" : metadata .get ("date" ),
128+ }
129+ )
130+
131+ html = template .render (
132+ page_url = config ["deploy_url" ] + page_url .lstrip ("/" ),
133+ title = metadata .get ("title" , slug .replace ("-" , " " ).title ()),
134+ subtitle = metadata .get ("subtitle" ),
135+ date = metadata .get ("date" ),
136+ content = html_content ,
137+ ** config ,
138+ )
139+
140+ with page_file .open ("w" ) as f :
141+ f .write (html )
142+
143+ # Build index after all posts (even if none)
144+ build_posts_index (posts_metadata , environment )
145+
146+
147+ def build_posts_index (posts_metadata : list [dict ], environment : Environment ):
148+ """Build an index page listing all posts."""
149+ template = environment .from_string (
150+ (template_dir / "posts_index.html" ).open ().read ()
151+ )
152+
153+ page_dir = build_dir / "p"
154+ page_dir .mkdir (exist_ok = True , parents = True )
155+
156+ html = template .render (
157+ page_url = config ["deploy_url" ] + "p/" ,
158+ title = "Posts" ,
159+ posts = sorted (posts_metadata , key = lambda x : x .get ("date" ) or "" , reverse = True ),
160+ ** config ,
161+ )
162+
163+ with (page_dir / "index.html" ).open ("w" ) as f :
164+ f .write (html )
165+
166+
84167def build_lab_manual ():
85168 name = config ["pages" ]["manual" ]["url" ][1 :- 1 ]
86169 environment = Environment (loader = FileSystemLoader (template_dir ))
0 commit comments