import { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType } from "docx"; import * as XLSX from "xlsx"; import { jsPDF } from "jspdf"; import pptxgen from "pptxgenjs"; export type DocType = "docx" | "xlsx" | "pdf" | "pptx"; export interface DocxOptions { title: string; subtitle?: string; sections: Array<{ heading?: string; body: string | string[]; }>; } export interface XlsxOptions { fileName: string; sheetName?: string; headers: string[]; rows: Array>; } export interface PdfOptions { title: string; subtitle?: string; author?: string; content: Array<{ heading?: string; text: string; }>; } export interface PptxSlide { title: string; bullets?: string[]; content?: string; keyTakeaway?: string; } export interface PptxOptions { title: string; subtitle?: string; slides: PptxSlide[]; } export interface GenerateDocResult { success: boolean; message: string; fileName?: string; fileUrl?: string; } // Check Tier Permissions for Document Creation export function validateDocTierPermission( docType: DocType, userTier: "guest" | "free" | "pro" | "tiniagents", requestedSlidesCount: number = 1 ): { allowed: boolean; reason?: string; maxSlidesAllowed?: number } { // Everything is 100% FREE for all users without charges or restrictions return { allowed: true, maxSlidesAllowed: 20 }; } // 1. Generate Word Document (.docx) export async function createWordDocument(options: DocxOptions): Promise { const children: Paragraph[] = []; // Document Title if (options.title) { children.push( new Paragraph({ text: options.title, heading: HeadingLevel.TITLE, alignment: AlignmentType.CENTER, spacing: { after: 200 } }) ); } // Subtitle if (options.subtitle) { children.push( new Paragraph({ children: [ new TextRun({ text: options.subtitle, italics: true, size: 24, color: "555555" }) ], alignment: AlignmentType.CENTER, spacing: { after: 400 } }) ); } // Sections options.sections.forEach((sec) => { if (sec.heading) { children.push( new Paragraph({ text: sec.heading, heading: HeadingLevel.HEADING_1, spacing: { before: 300, after: 120 } }) ); } if (Array.isArray(sec.body)) { sec.body.forEach((paraText) => { children.push( new Paragraph({ children: [new TextRun({ text: paraText, size: 22 })], spacing: { after: 150 } }) ); }); } else if (sec.body) { // Split by newlines if paragraph string has breaks const lines = sec.body.split("\n"); lines.forEach((line) => { if (line.trim().startsWith("- ") || line.trim().startsWith("* ")) { children.push( new Paragraph({ text: line.trim().substring(2), bullet: { level: 0 }, spacing: { after: 100 } }) ); } else if (line.trim()) { children.push( new Paragraph({ children: [new TextRun({ text: line.trim(), size: 22 })], spacing: { after: 150 } }) ); } }); } }); const doc = new Document({ sections: [ { properties: {}, children: children } ] }); return await Packer.toBlob(doc); } // 2. Generate Excel Spreadsheet (.xlsx) export function createExcelSpreadsheet(options: XlsxOptions): Blob { const wsData = [options.headers, ...options.rows]; const worksheet = XLSX.utils.aoa_to_sheet(wsData); // Set header column formatting auto-width const colWidths = options.headers.map((h, i) => { let maxLen = h.length; options.rows.forEach((r) => { const valStr = String(r[i] ?? ""); if (valStr.length > maxLen) maxLen = valStr.length; }); return { wch: Math.min(Math.max(maxLen + 4, 12), 40) }; }); worksheet["!cols"] = colWidths; const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, options.sheetName || "Sheet1"); const wbout = XLSX.write(workbook, { bookType: "xlsx", type: "array" }); return new Blob([wbout], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }); } // 3. Generate PDF Document (.pdf) export function createPdfDocument(options: PdfOptions): Blob { const doc = new jsPDF({ orientation: "portrait", unit: "mm", format: "a4" }); const pageHeight = doc.internal.pageSize.getHeight(); const pageWidth = doc.internal.pageSize.getWidth(); const margin = 15; const maxLineWidth = pageWidth - margin * 2; let y = 20; // Primary Header Title doc.setFont("helvetica", "bold"); doc.setFontSize(20); doc.setTextColor(30, 41, 59); // Slate-800 doc.text(options.title || "Document Report", margin, y); y += 10; if (options.subtitle) { doc.setFont("helvetica", "normal"); doc.setFontSize(12); doc.setTextColor(100, 116, 139); // Slate-500 doc.text(options.subtitle, margin, y); y += 10; } // Divider Line doc.setDrawColor(226, 232, 240); // Slate-200 doc.setLineWidth(0.5); doc.line(margin, y, pageWidth - margin, y); y += 10; options.content.forEach((sec) => { // Check page space for section header if (y > pageHeight - 25) { doc.addPage(); y = 20; } if (sec.heading) { doc.setFont("helvetica", "bold"); doc.setFontSize(14); doc.setTextColor(15, 23, 42); // Slate-900 doc.text(sec.heading, margin, y); y += 7; } doc.setFont("helvetica", "normal"); doc.setFontSize(10); doc.setTextColor(51, 65, 85); // Slate-700 const lines = doc.splitTextToSize(sec.text || "", maxLineWidth); lines.forEach((line: string) => { if (y > pageHeight - 20) { doc.addPage(); y = 20; } doc.text(line, margin, y); y += 5.5; }); y += 6; // Spacing after section }); // Footer Page Numbers const totalPages = doc.getNumberOfPages(); for (let i = 1; i <= totalPages; i++) { doc.setPage(i); doc.setFont("helvetica", "italic"); doc.setFontSize(8); doc.setTextColor(148, 163, 184); doc.text(`Page ${i} of ${totalPages} • Generated by Aura AI`, pageWidth / 2, pageHeight - 10, { align: "center" }); } return doc.output("blob"); } // 4. Generate PowerPoint Presentation (.pptx) export async function createPptxPresentation( options: PptxOptions, maxAllowedSlides: number = 10 ): Promise { const pptx = new pptxgen(); pptx.layout = "LAYOUT_16x9"; pptx.author = "Aura AI"; pptx.company = "Aura Cognitive Assistant"; // Title Slide const titleSlide = pptx.addSlide(); titleSlide.background = { color: "0F172A" }; // Dark Navy Blue titleSlide.addText(options.title || "Presentation", { x: 0.8, y: 2.0, w: "80%", h: 1.5, fontSize: 36, bold: true, color: "38BDF8", // Sky blue accent align: "left" }); if (options.subtitle) { titleSlide.addText(options.subtitle, { x: 0.8, y: 3.6, w: "80%", h: 1.0, fontSize: 20, color: "94A3B8", align: "left" }); } // Filter slides up to maximum allowed by tier (PRO: 5, TiniAgents: 10) const slidesToRender = (options.slides || []).slice(0, maxAllowedSlides); slidesToRender.forEach((slideData, idx) => { const slide = pptx.addSlide(); slide.background = { color: "F8FAFC" }; // Slate 50 background // Header Title Bar slide.addText(slideData.title || `Slide ${idx + 2}`, { x: 0.8, y: 0.6, w: "85%", h: 0.8, fontSize: 24, bold: true, color: "0F172A" }); // Content or Bullets if (slideData.bullets && slideData.bullets.length > 0) { const bulletItems = slideData.bullets.map((b) => ({ text: b, options: { fontSize: 16, color: "334155", breakLine: true, bullet: true } })); slide.addText(bulletItems, { x: 0.8, y: 1.6, w: "85%", h: 4.2, margin: 10 }); } else if (slideData.content) { slide.addText(slideData.content, { x: 0.8, y: 1.6, w: "85%", h: 4.2, fontSize: 16, color: "334155" }); } // Key Takeaway Box at bottom if present if (slideData.keyTakeaway) { slide.addText(`KEY TAKEAWAY: ${slideData.keyTakeaway}`, { x: 0.8, y: 6.0, w: "85%", h: 0.8, fontSize: 13, bold: true, color: "0369A1", fill: { color: "E0F2FE" }, align: "center" }); } }); const blob = (await pptx.write({ outputType: "blob" })) as Blob; return blob; } // Download Helper export function triggerFileDownload(blob: Blob, defaultFileName: string) { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = defaultFileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(() => URL.revokeObjectURL(url), 1000); }