@@ -283,11 +283,11 @@ async fn main() -> Result<()> {
283283 let temp_markdown = temp_dir. join ( format ! ( "{}_temp.md" , repo_name) ) ;
284284
285285 if needs_chunking {
286- process_files_in_chunks ( files, repo_name, effective_chunk_size, & temp_markdown, include_toc, verbose, memory_limit, max_file_size_mb) . await
286+ process_files_in_chunks ( & files, repo_name, effective_chunk_size, & temp_markdown, include_toc, verbose, memory_limit, max_file_size_mb) . await
287287 . context ( "Failed to process files in chunks" ) ?;
288288 } else {
289289 let markdown_generator = MarkdownGenerator :: new ( include_toc, true ) ;
290- let markdown_content = markdown_generator. generate_markdown ( files, repo_name)
290+ let markdown_content = markdown_generator. generate_markdown ( & files, repo_name)
291291 . context ( "Failed to generate markdown" ) ?;
292292 fs:: write ( & temp_markdown, & markdown_content)
293293 . context ( "Failed to write temporary markdown file" ) ?;
@@ -302,20 +302,42 @@ async fn main() -> Result<()> {
302302 }
303303
304304 // Configure Pandoc
305+ let use_chunked_pdf = matches ! ( output_format, OutputFormat :: Pdf ) &&
306+ ( files. len ( ) > 10 || total_size > 1_000_000 ) ;
307+
308+ if use_chunked_pdf && verbose {
309+ println ! ( "📦 Using chunked PDF processing for better memory efficiency" ) ;
310+ }
311+
305312 let pandoc_config = PandocConfig {
306313 output_format,
307314 highlight_style : theme,
308315 include_toc,
309316 syntax_definitions : Vec :: new ( ) ,
317+ use_chunked_pdf,
310318 } ;
311319
312320 let converter = PandocConverter :: new ( pandoc_config)
313321 . context ( "Failed to initialize Pandoc converter" ) ?;
314322
315323 // Convert to final format
316324 println ! ( "{}" , "🔄 Converting to final format..." . color( Color :: Cyan ) ) ;
317- converter. convert_markdown_to_document ( & temp_markdown, output_path, verbose) . await
318- . context ( "Failed to convert markdown to final format" ) ?;
325+
326+ if use_chunked_pdf {
327+ // For chunked PDF processing, we need individual markdown files
328+ let temp_dir = std:: env:: temp_dir ( ) ;
329+ let chunk_markdowns = generate_individual_markdowns ( & files, repo_name, include_toc, & temp_dir, verbose) ?;
330+ converter. convert_markdown_chunks_to_pdf ( & chunk_markdowns, output_path, verbose) . await
331+ . context ( "Failed to convert markdown chunks to PDF" ) ?;
332+
333+ // Clean up temporary markdown files
334+ for markdown_file in & chunk_markdowns {
335+ let _ = fs:: remove_file ( markdown_file) ;
336+ }
337+ } else {
338+ converter. convert_markdown_to_document ( & temp_markdown, output_path, verbose) . await
339+ . context ( "Failed to convert markdown to final format" ) ?;
340+ }
319341
320342 // Keep temporary file for debugging
321343 // let _ = fs::remove_file(&temp_markdown);
@@ -341,7 +363,7 @@ async fn main() -> Result<()> {
341363}
342364
343365async fn process_files_in_chunks (
344- files : Vec < FileInfo > ,
366+ files : & [ FileInfo ] ,
345367 repo_name : & str ,
346368 chunk_size : usize ,
347369 output_path : & Path ,
@@ -360,7 +382,7 @@ async fn process_files_in_chunks(
360382 // Add table of contents for all files
361383 if include_toc {
362384 final_markdown. push_str ( "## Table of Contents\n \n " ) ;
363- for file in & files {
385+ for file in files {
364386 let sanitized_path = file. path . replace ( [ '/' , '\\' ] , "-" ) . replace ( '.' , "-" ) ;
365387 let escaped_path = escape_markdown_special_chars ( & file. path ) ;
366388 final_markdown. push_str ( & format ! ( "- [{}](#{sanitized_path})\n " , escaped_path) ) ;
@@ -371,7 +393,7 @@ async fn process_files_in_chunks(
371393 // Add file tree
372394 final_markdown. push_str ( "## File Structure\n \n " ) ;
373395 final_markdown. push_str ( "```\n " ) ;
374- for file in & files {
396+ for file in files {
375397 final_markdown. push_str ( & format ! ( "{}\n " , file. path) ) ;
376398 }
377399 final_markdown. push_str ( "```\n \n " ) ;
@@ -382,7 +404,7 @@ async fn process_files_in_chunks(
382404 // Process files in chunks
383405 let chunks: Vec < & [ FileInfo ] > = files. chunks ( chunk_size) . collect ( ) ;
384406 let total_chunks = chunks. len ( ) ;
385- let mut global_page_number = 1 ; // Start after title/TOC page
407+ let mut _global_page_number = 1 ; // Start after title/TOC page
386408 let mut file_counter = 0 ;
387409
388410 for ( chunk_index, chunk) in chunks. iter ( ) . enumerate ( ) {
@@ -394,7 +416,7 @@ async fn process_files_in_chunks(
394416 // Process each file in the chunk
395417 for file in chunk. iter ( ) {
396418 file_counter += 1 ;
397- global_page_number += 1 ; // Each file gets a new page
419+ _global_page_number += 1 ; // Each file gets a new page
398420
399421 if verbose {
400422 sys. refresh_memory ( ) ;
@@ -432,8 +454,7 @@ async fn process_files_in_chunks(
432454
433455 // Add file header with page numbers
434456 final_markdown. push_str ( & format ! ( "### {} {{#{sanitized_path}}}\n \n " , escaped_path) ) ;
435- final_markdown. push_str ( & format ! ( "**File:** {} | **Size:** {} | **File #{} | Page {}**\n \n " ,
436- escaped_path, format_file_size( file. size) , file_counter, global_page_number) ) ;
457+ final_markdown. push_str ( & format ! ( "**Size:** {}\n \n " , format_file_size( file. size) ) ) ;
437458
438459 if let Some ( language) = & file. language {
439460 final_markdown. push_str ( & format ! ( "```{}\n " , language) ) ;
@@ -443,15 +464,12 @@ async fn process_files_in_chunks(
443464
444465 final_markdown. push_str ( & processed_content) ;
445466
446- if !file. content . ends_with ( '\n' ) {
467+ // Ensure there's always a newline before closing backticks
468+ if !processed_content. ends_with ( '\n' ) {
447469 final_markdown. push ( '\n' ) ;
448470 }
449471
450472 final_markdown. push_str ( "```\n \n " ) ;
451-
452- // Add file info with page numbering
453- final_markdown. push_str ( & format ! ( "*File size: {} | File #{} of {} | Page {}*\n \n " ,
454- format_file_size( file. size) , file_counter, files. len( ) , global_page_number) ) ;
455473 final_markdown. push_str ( "---\n \n " ) ;
456474 }
457475
@@ -594,6 +612,77 @@ fn list_themes() -> Result<()> {
594612 Ok ( ( ) )
595613}
596614
615+ fn generate_individual_markdowns ( files : & [ FileInfo ] , repo_name : & str , include_toc : bool , temp_dir : & Path , verbose : bool ) -> Result < Vec < PathBuf > > {
616+ let mut markdown_files = Vec :: new ( ) ;
617+
618+ // Generate title page markdown
619+ let title_markdown = temp_dir. join ( "scrollcast_title.md" ) ;
620+ let mut title_content = String :: new ( ) ;
621+ title_content. push_str ( & format ! ( "# {}\n \n " , repo_name) ) ;
622+ title_content. push_str ( & format ! ( "Generated on: {}\n \n " , chrono:: Utc :: now( ) . format( "%Y-%m-%d %H:%M:%S UTC" ) ) ) ;
623+
624+ if include_toc {
625+ title_content. push_str ( "## Table of Contents\n \n " ) ;
626+ for file in files {
627+ let escaped_path = escape_markdown_special_chars ( & file. path ) ;
628+ title_content. push_str ( & format ! ( "- {}\n " , escaped_path) ) ;
629+ }
630+ title_content. push_str ( "\n " ) ;
631+ }
632+
633+ // Add file tree
634+ title_content. push_str ( "## File Structure\n \n " ) ;
635+ title_content. push_str ( "```\n " ) ;
636+ for file in files {
637+ title_content. push_str ( & format ! ( "{}\n " , file. path) ) ;
638+ }
639+ title_content. push_str ( "```\n \n " ) ;
640+
641+ fs:: write ( & title_markdown, & title_content)
642+ . context ( "Failed to write title markdown" ) ?;
643+ markdown_files. push ( title_markdown) ;
644+
645+ if verbose {
646+ println ! ( " 📄 Generated title page" ) ;
647+ }
648+
649+ // Generate individual file markdowns
650+ for ( index, file) in files. iter ( ) . enumerate ( ) {
651+ let file_markdown = temp_dir. join ( format ! ( "scrollcast_file_{}.md" , index) ) ;
652+ let mut file_content = String :: new ( ) ;
653+
654+ let sanitized_path = file. path . replace ( [ '/' , '\\' ] , "-" ) . replace ( '.' , "-" ) ;
655+ let escaped_path = escape_markdown_special_chars ( & file. path ) ;
656+ file_content. push_str ( & format ! ( "## {} {{#{sanitized_path}}}\n \n " , escaped_path) ) ;
657+ file_content. push_str ( & format ! ( "**Size:** {}\n \n " , format_file_size( file. size) ) ) ;
658+
659+ if let Some ( language) = & file. language {
660+ file_content. push_str ( & format ! ( "```{}\n " , language) ) ;
661+ } else {
662+ file_content. push_str ( "```\n " ) ;
663+ }
664+
665+ let processed_content = process_content_for_latex ( & file. content ) ;
666+ file_content. push_str ( & processed_content) ;
667+
668+ if !processed_content. ends_with ( '\n' ) {
669+ file_content. push ( '\n' ) ;
670+ }
671+
672+ file_content. push_str ( "```\n \n " ) ;
673+
674+ fs:: write ( & file_markdown, & file_content)
675+ . context ( "Failed to write file markdown" ) ?;
676+ markdown_files. push ( file_markdown) ;
677+
678+ if verbose {
679+ println ! ( " 📄 Generated markdown for: {}" , file. path) ;
680+ }
681+ }
682+
683+ Ok ( markdown_files)
684+ }
685+
597686fn list_languages ( ) -> Result < ( ) > {
598687 println ! ( "{}" , "Supported programming languages:" . color( Color :: Blue ) . bold( ) ) ;
599688
0 commit comments