@@ -12,7 +12,7 @@ import { checkForUpdates } from '../lib/version-check.js';
1212interface RenderOptions {
1313 props ?: string ;
1414 out ?: string ;
15- format ?: 'png' | 'svg' | 'webp' | 'jpg' | 'jpeg' | 'mp4' | 'gif' ;
15+ format ?: 'png' | 'svg' | 'webp' | 'jpg' | 'jpeg' | 'mp4' | 'gif' | 'pptx' | 'pdf' ;
1616 framesOnly ?: boolean ;
1717 crf ?: number ;
1818 ffmpeg ?: boolean ;
@@ -32,7 +32,7 @@ export async function renderCommand(
3232 const result = await updateCheck ;
3333 if ( result ?. isOutdated ) {
3434 console . log ( chalk . dim ( `Update available: ${ result . currentVersion } → ${ chalk . green ( result . latestVersion ) } ` ) ) ;
35- console . log ( chalk . dim ( `Run: ${ chalk . cyan ( 'npm install -g loopwind@latest ' ) } \n` ) ) ;
35+ console . log ( chalk . dim ( `Run: ${ chalk . cyan ( 'curl -fsSL https:// loopwind.dev/install.sh | bash ' ) } \n` ) ) ;
3636 }
3737 } ;
3838
@@ -66,14 +66,68 @@ export async function renderCommand(
6666 const meta = await loadTemplateMeta ( templateName ) ;
6767 const templateType = meta . type || 'image' ;
6868
69- // Check for unsupported types
70- if ( templateType = == 'presentation' ) {
71- spinner . fail ( chalk . yellow ( 'Presentation rendering is not yet implemented' ) ) ;
72- console . log ( chalk . dim ( `\nPresentation rendering will be available in a future release .` ) ) ;
73- console . log ( chalk . dim ( `Presentations will export multi-page PDFs with React + Tailwind .\n` ) ) ;
69+ // Guard: --format pptx on non-presentation template
70+ if ( options . format === 'pptx' && templateType ! == 'presentation' ) {
71+ spinner . fail ( chalk . red ( `--format pptx is only supported for presentation templates` ) ) ;
72+ console . log ( chalk . dim ( `\nTemplate " ${ templateName } " is type " ${ templateType } " .` ) ) ;
73+ console . log ( chalk . dim ( `To create a presentation, set meta.type = "presentation" and define meta.presentation.slides .\n` ) ) ;
7474 process . exit ( 1 ) ;
7575 }
7676
77+ // Handle presentation templates
78+ if ( templateType === 'presentation' ) {
79+ const { renderPptx } = await import ( '../lib/pptx-renderer.js' ) ;
80+
81+ // Parse props
82+ spinner . text = 'Loading props...' ;
83+ const propsInput = propsArg || options . props ;
84+ const props = await parseProps ( propsInput ) ;
85+
86+ // Validate template and props
87+ spinner . text = 'Validating template...' ;
88+ const validation = await validateTemplateForRendering ( templateName , props ) ;
89+
90+ if ( ! validation . valid ) {
91+ spinner . fail ( chalk . red ( 'Template validation failed' ) ) ;
92+ console . log ( ) ;
93+ for ( const error of validation . errors ) {
94+ console . log ( chalk . red ( ` ✗ ${ error . field } : ${ error . message } ` ) ) ;
95+ if ( error . suggestion ) {
96+ console . log ( chalk . dim ( ` → ${ error . suggestion } ` ) ) ;
97+ }
98+ console . log ( ) ;
99+ }
100+ console . log ( chalk . yellow ( 'Fix the errors above and try again.\n' ) ) ;
101+ process . exit ( 1 ) ;
102+ }
103+
104+ const baseName = templateName . endsWith ( '.tsx' )
105+ ? path . basename ( templateName , '.tsx' )
106+ : templateName ;
107+ const outputPath = options . out || `${ baseName } .pptx` ;
108+
109+ spinner . text = 'Rendering presentation slides...' ;
110+ const slideCount = meta . presentation ?. slides ?. length ?? 0 ;
111+
112+ await renderPptx ( templateName , props , outputPath , {
113+ onProgress : ( slide , total , phase ) => {
114+ if ( phase === 'render' ) {
115+ spinner . text = `Rendering slide ${ slide } /${ total } ...` ;
116+ } else {
117+ spinner . text = `Assembling PPTX (${ total } slides)...` ;
118+ }
119+ } ,
120+ } ) ;
121+
122+ spinner . succeed ( chalk . green ( `Successfully rendered PPTX to ${ chalk . bold ( outputPath ) } ` ) ) ;
123+ console . log ( chalk . dim ( `\nTemplate: ${ templateName } ` ) ) ;
124+ console . log ( chalk . dim ( `Slides: ${ slideCount } ` ) ) ;
125+ console . log ( chalk . dim ( `Output: ${ path . resolve ( outputPath ) } \n` ) ) ;
126+
127+ await showUpdateNotification ( ) ;
128+ return ;
129+ }
130+
77131 if ( templateType === 'website' ) {
78132 spinner . fail ( chalk . yellow ( 'Website rendering is not yet implemented' ) ) ;
79133 console . log ( chalk . dim ( `\nWebsite rendering will be available in a future release.` ) ) ;
@@ -210,9 +264,27 @@ export async function renderCommand(
210264 return ;
211265 }
212266
267+ // Handle PDF format for image templates
268+ if ( options . format === 'pdf' ) {
269+ const { renderPdf } = await import ( '../lib/pdf-renderer.js' ) ;
270+
271+ const outputPath = options . out || `${ baseName } .pdf` ;
272+
273+ spinner . text = 'Rendering to PDF...' ;
274+ await renderPdf ( templateName , props , outputPath ) ;
275+
276+ spinner . succeed ( chalk . green ( `Successfully rendered PDF to ${ chalk . bold ( outputPath ) } ` ) ) ;
277+ console . log ( chalk . dim ( `\nTemplate: ${ templateName } ` ) ) ;
278+ console . log ( chalk . dim ( `Format: PDF` ) ) ;
279+ console . log ( chalk . dim ( `Output: ${ path . resolve ( outputPath ) } \n` ) ) ;
280+
281+ await showUpdateNotification ( ) ;
282+ return ;
283+ }
284+
213285 // Handle image templates
214286 // Filter out video formats for image templates
215- const imageFormat = ( options . format === 'mp4' || options . format === 'gif' )
287+ const imageFormat = ( options . format === 'mp4' || options . format === 'gif' || options . format === 'pptx' )
216288 ? 'png'
217289 : ( options . format || 'png' ) ;
218290 const defaultFileName = `${ baseName } .${ imageFormat } ` ;
0 commit comments