@@ -37,7 +37,7 @@ def generate_output_from_typst(code, output_path, format="svg", cetz_path=DEFAUL
3737 if result .returncode != 0 :
3838 print (f"Error generating { format .upper ()} : { result .stderr } " )
3939 return False
40-
40+
4141 return True
4242 except Exception as e :
4343 print (f"Exception generating { format .upper ()} : { e } " )
@@ -58,7 +58,7 @@ def convert_typst_to_html_content(text):
5858"""
5959 tmp .write (typst_content )
6060 tmp .flush ()
61-
61+
6262 with tempfile .NamedTemporaryFile (suffix = '.html' , delete = True ) as html_tmp :
6363 result = subprocess .run (
6464 ["typst" , "compile" , tmp .name , html_tmp .name ,
@@ -69,19 +69,19 @@ def convert_typst_to_html_content(text):
6969 if result .returncode != 0 :
7070 print (f"Error generating HTML: { result .stderr } " )
7171 return None
72-
72+
7373 # Read the generated HTML and extract body content
7474 with open (html_tmp .name , 'r' ) as f :
7575 html_content = f .read ()
76-
76+
7777 # Extract content between <body> tags
7878 import re
7979 body_match = re .search (r'<body[^>]*>(.*?)</body>' , html_content , re .DOTALL )
8080 if body_match :
8181 return body_match .group (1 ).strip ()
8282 else :
8383 return html_content
84-
84+
8585 except Exception as e :
8686 print (f"Exception converting Typst to HTML: { e } " )
8787 return None
@@ -91,14 +91,14 @@ def html_to_mdx(html_content):
9191 """Convert HTML content to MDX-compatible format."""
9292 if not html_content :
9393 return ""
94-
94+
9595 # Clean up the HTML for MDX compatibility:
9696 # - Remove style attributes
9797 # - Convert self closing to closing
9898 html_content = re .sub (r'\s+style="[^"]*"' , '' , html_content )
9999 html_content = re .sub (r'<br>' , '<br />' , html_content )
100100 html_content = re .sub (r'<hr>' , '<hr />' , html_content )
101-
101+
102102 return html_content .strip ()
103103
104104
@@ -125,10 +125,10 @@ def generate_mdx_file(func_data, output_path, cetz_path=DEFAULT_CETZ_VERSION):
125125 comment = func_data .get ("comment" , {})
126126 signature = func_data .get ("signature" , {})
127127 func_name = signature .get ("name" , "unknown" ) if signature else "unknown"
128-
128+
129129 # Start MDX content
130130 mdx_lines = []
131-
131+
132132 # Build parameters object for Function component
133133 parameters = {}
134134 arguments = comment .get ("arguments" , [])
@@ -142,24 +142,24 @@ def generate_mdx_file(func_data, output_path, cetz_path=DEFAULT_CETZ_VERSION):
142142 default_value = default_value [2 :]
143143 param_info ["default" ] = default_value
144144 parameters [name ] = param_info
145-
145+
146146 # Function component
147147 import json
148148 parameters_json = json .dumps (parameters )
149149 mdx_lines .append (f'<Function name="{ func_name } " parameters={{{ parameters_json } }}/>' .replace ('null' , 'undefined' ))
150-
150+
151151 # Convert main text through Typst -> HTML -> MDX
152152 main_text = comment .get ("text" , "" )
153153 if main_text :
154154 # Remove example blocks from main text for separate processing
155155 text_without_examples = re .sub (r'```(?:typc?\s+)?(?:example|example-vertical)\s*\n.*?```' , '' , main_text , flags = re .DOTALL )
156-
156+
157157 html_content = convert_typst_to_html_content (text_without_examples )
158158 if html_content :
159159 mdx_content = html_to_mdx (html_content )
160160 mdx_lines .append (mdx_content )
161161 mdx_lines .append ('' )
162-
162+
163163 # Add example blocks
164164 examples = extract_example_blocks (main_text )
165165 if examples :
@@ -168,15 +168,15 @@ def generate_mdx_file(func_data, output_path, cetz_path=DEFAULT_CETZ_VERSION):
168168 mdx_lines .append (example_code )
169169 mdx_lines .append ('```' )
170170 mdx_lines .append ('' )
171-
171+
172172 # Add parameter documentation
173173 if arguments :
174174 for arg in arguments :
175175 name = arg .get ("name" , "" )
176176 types = format_types (arg .get ("types" , []))
177177 default_value = arg .get ("default-value" , "" )
178178 text = arg .get ("text" , "" )
179-
179+
180180 if name :
181181 param_attrs = [f'name="{ name } "' ]
182182 if types :
@@ -185,19 +185,19 @@ def generate_mdx_file(func_data, output_path, cetz_path=DEFAULT_CETZ_VERSION):
185185 if default_value .startswith ("= " ):
186186 default_value = default_value [2 :]
187187 param_attrs .append (f'default_value="{ escape_html_attr (default_value )} "' )
188-
188+
189189 mdx_lines .append (f'<Parameter { " " .join (param_attrs )} >' )
190-
190+
191191 if text :
192192 # Convert parameter description through Typst -> HTML -> MDX
193193 html_content = convert_typst_to_html_content (text )
194194 if html_content :
195195 mdx_content = html_to_mdx (html_content )
196196 mdx_lines .append (mdx_content )
197-
197+
198198 mdx_lines .append ('</Parameter>' )
199199 mdx_lines .append ('' )
200-
200+
201201 try :
202202 with open (output_path , 'w' ) as f :
203203 f .write ('\n ' .join (mdx_lines ))
@@ -211,33 +211,33 @@ def extract_example_blocks(text):
211211 """Extract all example code blocks from text with proper indentation."""
212212 if not text :
213213 return []
214-
214+
215215 # Find all ```example or ```example-vertical blocks
216216 pattern = r'```(?:typc?\s+)?(?:example|example-vertical)\s*\n(.*?)```'
217217 matches = re .findall (pattern , text , re .DOTALL )
218-
218+
219219 cleaned_blocks = []
220220 for match in matches :
221221 lines = match .split ('\n ' )
222222 if not lines :
223223 continue
224-
224+
225225 # Remove leading/trailing empty lines
226226 while lines and not lines [0 ].strip ():
227227 lines .pop (0 )
228228 while lines and not lines [- 1 ].strip ():
229229 lines .pop ()
230-
230+
231231 if not lines :
232232 continue
233-
233+
234234 # Find the minimum indentation (excluding empty lines)
235235 min_indent = float ('inf' )
236236 for line in lines :
237237 if line .strip (): # Skip empty lines
238238 indent = len (line ) - len (line .lstrip ())
239239 min_indent = min (min_indent , indent )
240-
240+
241241 # Remove the common indentation from all lines
242242 if min_indent != float ('inf' ) and min_indent > 0 :
243243 dedented_lines = []
@@ -249,7 +249,7 @@ def extract_example_blocks(text):
249249 cleaned_blocks .append ('\n ' .join (dedented_lines ))
250250 else :
251251 cleaned_blocks .append ('\n ' .join (lines ))
252-
252+
253253 return cleaned_blocks
254254
255255
@@ -259,7 +259,7 @@ def main():
259259 description = 'Generate MDX documentation from CeTZ docs with optional SVG examples'
260260 )
261261 parser .add_argument (
262- 'json_file' ,
262+ 'json_file' ,
263263 nargs = '?' ,
264264 help = 'Path to docs.json file (reads from stdin if not provided)'
265265 )
@@ -281,16 +281,16 @@ def main():
281281 const = 'svg_output' ,
282282 help = 'Generate SVG files from examples. Optional: specify output directory (default: svg_output)'
283283 )
284-
284+
285285 args = parser .parse_args ()
286-
286+
287287 # Load JSON data
288288 if args .json_file :
289289 json_file = Path (args .json_file )
290290 if not json_file .exists ():
291291 print (f"Error: { json_file } not found." )
292292 sys .exit (1 )
293-
293+
294294 with open (json_file , 'r' ) as f :
295295 data = json .load (f )
296296 else :
@@ -300,80 +300,80 @@ def main():
300300 except :
301301 print ("Error: Could not read from stdin." )
302302 sys .exit (1 )
303-
303+
304304 mdx_dir = Path (args .output )
305305 mdx_dir .mkdir (exist_ok = True )
306306 svg_dir = None
307307 if args .svg :
308308 svg_dir = Path (args .svg )
309309 svg_dir .mkdir (exist_ok = True )
310-
310+
311311 if isinstance (data , list ) and len (data ) > 0 :
312312 data = data [0 ]
313-
313+
314314 # Process each file in the JSON
315315 for file_path , functions in data .items ():
316316 print (f"\n Processing { file_path } " )
317-
317+
318318 # Create directory structure matching source
319319 # e.g., "src/draw/shapes.typ" -> "draw/shapes/"
320320 path_parts = file_path .replace ("src/" , "" ).replace (".typ" , "" ).split ("/" )
321-
321+
322322 current_mdx_dir = mdx_dir
323323 for part in path_parts :
324324 current_mdx_dir = current_mdx_dir / part
325325 current_mdx_dir .mkdir (exist_ok = True )
326-
326+
327327 file_base = file_path .replace ("src/" , "" ).replace ("/" , "_" ).replace (".typ" , "" )
328-
328+
329329 # Track functions for combined file generation
330330 function_names = []
331331 for func_data in functions :
332332 signature = func_data .get ("signature" , {})
333333 func_name = signature .get ("name" , "unknown" ) if signature else "unknown"
334334 comment = func_data .get ("comment" , {})
335335 text = comment .get ("text" , "" )
336-
336+
337337 if not func_name .startswith ("_" ):
338338 function_names .append (func_name )
339-
339+
340340 # Generate MDX files
341341 mdx_filename = f"{ func_name } .mdx"
342342 mdx_path = current_mdx_dir / mdx_filename
343-
343+
344344 if generate_mdx_file (func_data , mdx_path , args .cetz ):
345345 print (f"[ OK] Generated MDX: { '/' .join (path_parts )} /{ mdx_filename } " )
346346 else :
347347 print (f"[ERROR] Failed to generate MDX: { '/' .join (path_parts )} /{ mdx_filename } " )
348-
348+
349349 # Generate SVGs
350350 if svg_dir :
351351 examples = extract_example_blocks (text )
352-
352+
353353 if examples :
354354 for i , example_code in enumerate (examples ):
355355 svg_filename = f"{ file_base } _{ func_name } _{ i } .svg"
356356 svg_path = svg_dir / svg_filename
357-
357+
358358 if generate_output_from_typst (example_code , svg_path , "svg" , args .cetz ):
359359 print (f"[ OK] Generated SVG: { svg_filename } ({ i + 1 } /{ len (examples )} )" )
360360 else :
361361 print (f"[ERROR] Failed to generate SVG: { svg_filename } " )
362-
362+
363363 # Generate combined MDX file
364364 if function_names :
365365 #combined_filename = f"{path_parts[-1]}-combined.mdx"
366366 combined_filename = f"-combined.mdx"
367367 combined_path = current_mdx_dir / combined_filename
368-
368+
369369 combined_imports = []
370370 combined_lines = []
371371 for func_name in function_names :
372372 import_name = func_name .replace ("-" , "" ).upper ()
373373 combined_imports .append (f'import { import_name } from "./{ func_name } .mdx"' )
374374 combined_lines .append (f'## { func_name } ' )
375375 combined_lines .append (f'<{ import_name } />\n ' )
376-
376+
377377 try :
378378 with open (combined_path , 'w' ) as f :
379379 f .write ('\n ' .join (combined_imports ) + '\n \n ' + '\n ' .join (combined_lines ) + '\n ' )
0 commit comments