File size: 5,144 Bytes
2d7440e
 
 
 
 
 
 
 
eac938a
 
2d7440e
 
 
 
eac938a
 
 
2d7440e
 
 
eac938a
2d7440e
eac938a
2d7440e
eac938a
 
2d7440e
 
 
eac938a
 
2d7440e
 
eac938a
 
 
 
2d7440e
eac938a
 
2d7440e
 
eac938a
 
 
2d7440e
 
 
eac938a
 
2d7440e
eac938a
 
 
 
 
 
2d7440e
 
eac938a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2d7440e
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import PptxGenJS from 'pptxgenjs';
import { PitchDeckData, SlideData } from '../ai/types';
import { DocumentRenderer } from './types';

export class PptxDeckRenderer implements DocumentRenderer<PitchDeckData> {
    async render(data: PitchDeckData): Promise<Buffer> {
        const pres = new PptxGenJS();

        // 🎨 Premium Design System
        // XAMLÉ Palette: Emerald (#1C7C54), Navy (#1B3A57), Saffron (#F4A261)
        pres.defineSlideMaster({
            title: 'MASTER_SLIDE',
            bkgd: 'FFFFFF',
            objects: [
                { rect: { x: 0, y: 0, w: '100%', h: 1.0, fill: { color: '1B3A57' } } }, // Header
                { rect: { x: 0, y: 1.0, w: '100%', h: 0.05, fill: { color: 'F4A261' } } }, // Accent line
                { text: { text: "XAMLÉ 🇸🇳 - Pitch Deck Stratégique", options: { x: 0.5, y: 5.15, fontSize: 10, color: '1B3A57' } } }
            ]
        });

        // 1. Title Slide (Slide 1)
        const titleSlide = pres.addSlide();
        titleSlide.bkgd = '1B3A57';
        titleSlide.addText(data.title.toUpperCase(), {
            x: 0, y: 2, w: '100%', h: 1,
            fontSize: 48, color: 'FFFFFF', bold: true, fontFace: 'Montserrat', align: 'center'
        });
        if (data.subtitle) {
            titleSlide.addText(data.subtitle, {
                x: 0, y: 3.2, w: '100%', h: 1,
                fontSize: 22, color: 'F4A261', fontFace: 'Inter', align: 'center', italic: true
            });
        }
        titleSlide.addText("Propulsé par XAMLÉ AI", {
            x: 0, y: 4.8, w: '100%', h: 0.5,
            fontSize: 14, color: 'FFFFFF', fontFace: 'Inter', align: 'center'
        });

        // 2. Content Slides
        data.slides.forEach((slideData: SlideData, index: number) => {
            const slide = pres.addSlide({ masterName: 'MASTER_SLIDE' });

            // Slide Title (Montserrat Bold, White on Navy)
            slide.addText(slideData.title.toUpperCase(), {
                x: 0.5, y: 0.25, w: '90%', h: 0.5,
                fontSize: 28, color: 'FFFFFF', bold: true, fontFace: 'Montserrat'
            });

            const hasVisual = slideData.visualType && slideData.visualType !== 'NONE' && slideData.visualData;
            const textWidth = hasVisual ? '50%' : '90%';

            // 📝 Storytelling Text Blocks (Inter)
            const textBlocks = slideData.content.map(text => ({ text: text + '\n', options: { breakLine: true } }));
            slide.addText(textBlocks, {
                x: 0.5, y: 1.3, w: textWidth, h: '75%',
                fontSize: 15, color: '2D3748', fontFace: 'Inter',
                valign: 'top', margin: 5, lineSpacing: 22
            });

            // 📊 Graphical Integration (WOW Effect - Flat Design)
            if (hasVisual) {
                try {
                    const vizData = slideData.visualData;
                    if (slideData.visualType === 'PIE_CHART' && vizData.labels && vizData.values) {
                        const chartData = [{ name: 'Market Scale', labels: vizData.labels, values: vizData.values }];
                        slide.addChart(pres.ChartType.pie, chartData, {
                            x: 5.2, y: 1.2, w: 4.5, h: 4.0,
                            showLegend: true, legendPos: 'r',
                            showValue: true, showPercent: true,
                            chartColors: ['1C7C54', 'F4A261', '1B3A57'],
                            dataLabelColor: 'FFFFFF'
                        });
                    }
                    else if (slideData.visualType === 'BAR_CHART' && vizData.labels && vizData.values) {
                        const chartData = [{ name: 'Projections (FCFA)', labels: vizData.labels, values: vizData.values }];
                        slide.addChart(pres.ChartType.bar, chartData, {
                            x: 5.2, y: 1.2, w: 4.5, h: 4.0,
                            barDir: 'col',
                            showValue: true,
                            chartColors: ['1C7C54', 'F4A261', '1B3A57'],
                            dataLabelColor: '2D3748',
                            catAxisLabelColor: '2D3748',
                            valAxisLabelColor: '2D3748'
                        });
                    }
                    else if (slideData.visualType === 'IMAGE' && typeof vizData === 'string' && vizData.startsWith('http')) {
                        slide.addImage({ path: vizData, x: 5.2, y: 1.0, w: 4.5, h: 4.2 });
                    }
                    else if (slideData.visualType === 'IMAGE') {
                        slide.addText("📷 [Visual AI Placeholder]", { x: 6.5, y: 2.5, fontSize: 14, color: 'A0AEC0', fontFace: 'Inter' });
                    }
                } catch (vErr) {
                    console.warn(`[RENDERER] Failed to add visual to slide ${index + 1}:`, vErr);
                }
            }

            // Speaker Notes
            if (slideData.notes) {
                slide.addNotes(slideData.notes);
            }
        });

        const buffer = await pres.write({ outputType: 'nodebuffer' }) as Buffer;
        return buffer;
    }
}