Spaces:
Running
Running
Download src/utils/documentGenerator.ts from Aura-Studio/AI: direct link, hf CLI and curl.
- Browser
- Download file 9.27 kB
-
https://huggingface.co/spaces/Aura-Studio/AI/resolve/main/src/utils/documentGenerator.ts
- Command line
-
hf download hf://spaces/Aura-Studio/AI/src/utils/documentGenerator.ts
-
curl -L -o documentGenerator.ts https://huggingface.co/spaces/Aura-Studio/AI/resolve/main/src/utils/documentGenerator.ts
9.27 kB
| 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<Array<string | number | boolean>>; | |
| } | |
| 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<Blob> { | |
| 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<Blob> { | |
| 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); | |
| } | |