Files
anything-llm/server/utils/agents/aibitat/plugins/create-files/pptx/utils.js
Timothy Carambat 7aaea7f514 File creation agent skills (#5280)
* Powerpoint File Creation (#5278)

* wip

* download card

* UI for downloading

* move to fs system with endpoint to pull files

* refactor UI

* final-pass

* remove save-file-browser skill and refactor

* remove fileDownload event

* reset

* reset file

* reset timeout

* persist toggle

* Txt creation (#5279)

* wip

* download card

* UI for downloading

* move to fs system with endpoint to pull files

* refactor UI

* final-pass

* remove save-file-browser skill and refactor

* remove fileDownload event

* reset

* reset file

* reset timeout

* wip

* persist toggle

* add arbitrary text creation file

* Add PDF document generation with markdown formatting (#5283)

add support for branding in bottom right corner
refactor core utils and frontend rendering

* Xlsx document creation (#5284)

add Excel doc & sheet creation

* Basic docx creation (#5285)

* Basic docx creation

* add test theme support + styling and title pages

* simplify skill selection

* handle TG attachments

* send documents over tg

* lazy import

* pin deps

* fix lock

* i18n for file creation (#5286)

i18n for file-creation
connect #5280

* theme overhaul

* Add PPTX subagent for better results

* forgot files

* Add PPTX subagent for better results (#5287)

* Add PPTX subagent for better results

* forgot files

* make sub-agent use proper tool calling if it can and better UI hints
2026-03-30 15:13:39 -07:00

379 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const createFilesLib = require("../lib.js");
// All positioning assumes LAYOUT_16x9: 10 × 5.625 in.
const MARGIN_X = 0.7;
const CONTENT_W = 8.6; // 10 - 2 × MARGIN_X
const SLIDE_H = 5.625;
function isDarkColor(hexColor) {
const hex = (hexColor || "FFFFFF").replace("#", "");
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 < 0.5;
}
function addBranding(slide, bgColor) {
const isDark = isDarkColor(bgColor);
const textColor = isDark ? "FFFFFF" : "000000";
const logo = createFilesLib.getLogo({
forDarkBackground: isDark,
format: "dataUri",
});
slide.addText("Created with", {
x: 7.85,
y: 5.06,
w: 1.85,
h: 0.12,
fontSize: 5.5,
color: textColor,
transparency: 78,
fontFace: "Calibri",
align: "center",
italic: true,
});
if (logo) {
slide.addImage({
data: logo,
x: 8.025,
y: 5.17,
w: 1.5,
h: 0.24,
transparency: 78,
});
} else {
slide.addText("AnythingLLM", {
x: 7.85,
y: 5.17,
w: 1.85,
h: 0.24,
fontSize: 8,
color: textColor,
transparency: 78,
fontFace: "Calibri",
align: "center",
});
}
}
function addTopAccentBar(slide, pptx, theme) {
slide.addShape(pptx.ShapeType.rect, {
x: 0,
y: 0,
w: "100%",
h: 0.05,
fill: { color: theme.accentColor },
line: { color: theme.accentColor },
});
}
function addAccentUnderline(slide, pptx, x, y, color) {
slide.addShape(pptx.ShapeType.rect, {
x,
y,
w: 1.5,
h: 0.035,
fill: { color },
line: { color },
});
}
function addSlideFooter(slide, pptx, theme, slideNumber, totalSlides) {
slide.addShape(pptx.ShapeType.rect, {
x: MARGIN_X,
y: 5.0,
w: CONTENT_W,
h: 0.007,
fill: { color: theme.footerLineColor },
line: { color: theme.footerLineColor },
});
slide.addText(`${slideNumber} / ${totalSlides}`, {
x: MARGIN_X,
y: 5.07,
w: 1.2,
h: 0.25,
fontSize: 8,
color: theme.footerColor,
fontFace: theme.fontBody,
align: "left",
});
}
function renderTitleSlide(slide, pptx, { title, author }, theme) {
slide.background = { color: theme.titleSlideBackground };
slide.addText(title || "Untitled", {
x: 1.0,
y: 1.3,
w: 8.0,
h: 1.4,
fontSize: 36,
bold: true,
color: theme.titleSlideTitleColor,
fontFace: theme.fontTitle,
align: "center",
valign: "bottom",
});
addAccentUnderline(slide, pptx, 4.25, 2.9, theme.titleSlideAccentColor);
if (author) {
slide.addText(author, {
x: 1.5,
y: 3.15,
w: 7.0,
h: 0.45,
fontSize: 14,
color: theme.titleSlideSubtitleColor,
fontFace: theme.fontBody,
align: "center",
italic: true,
});
}
// Bottom accent strip
slide.addShape(pptx.ShapeType.rect, {
x: 0,
y: SLIDE_H - 0.1,
w: "100%",
h: 0.1,
fill: { color: theme.titleSlideAccentColor },
line: { color: theme.titleSlideAccentColor },
});
addBranding(slide, theme.titleSlideBackground);
}
function renderSectionSlide(
slide,
pptx,
slideData,
theme,
slideNumber,
totalSlides
) {
slide.background = { color: theme.titleSlideBackground };
slide.addText(slideData.title || "", {
x: 1.0,
y: 1.5,
w: 8.0,
h: 1.2,
fontSize: 32,
bold: true,
color: theme.titleSlideTitleColor,
fontFace: theme.fontTitle,
align: "center",
valign: "bottom",
});
addAccentUnderline(slide, pptx, 4.25, 2.9, theme.titleSlideAccentColor);
if (slideData.subtitle) {
slide.addText(slideData.subtitle, {
x: 1.5,
y: 3.1,
w: 7.0,
h: 0.5,
fontSize: 16,
color: theme.titleSlideSubtitleColor,
fontFace: theme.fontBody,
align: "center",
});
}
const numColor = isDarkColor(theme.titleSlideBackground)
? "FFFFFF"
: "000000";
slide.addText(`${slideNumber} / ${totalSlides}`, {
x: MARGIN_X,
y: 5.1,
w: 1.2,
h: 0.25,
fontSize: 8,
color: numColor,
transparency: 65,
fontFace: theme.fontBody,
align: "left",
});
addBranding(slide, theme.titleSlideBackground);
if (slideData.notes) slide.addNotes(slideData.notes);
}
function renderContentSlide(
slide,
pptx,
slideData,
theme,
slideNumber,
totalSlides
) {
slide.background = { color: theme.background };
addTopAccentBar(slide, pptx, theme);
let contentStartY = 0.4;
if (slideData.title) {
slide.addText(slideData.title, {
x: MARGIN_X,
y: 0.3,
w: CONTENT_W,
h: 0.65,
fontSize: 24,
bold: true,
color: theme.titleColor,
fontFace: theme.fontTitle,
valign: "bottom",
});
contentStartY = 1.0;
if (slideData.subtitle) {
slide.addText(slideData.subtitle, {
x: MARGIN_X,
y: 1.0,
w: CONTENT_W,
h: 0.3,
fontSize: 13,
color: theme.subtitleColor,
fontFace: theme.fontBody,
});
contentStartY = 1.35;
}
addAccentUnderline(
slide,
pptx,
MARGIN_X,
contentStartY + 0.05,
theme.accentColor
);
contentStartY += 0.25;
}
const footerY = 5.0;
const contentHeight = footerY - contentStartY - 0.15;
if (slideData.table) {
addTableContent(slide, pptx, slideData.table, theme, contentStartY);
} else {
addBulletContent(
slide,
slideData.content,
theme,
contentStartY,
contentHeight
);
}
addSlideFooter(slide, pptx, theme, slideNumber, totalSlides);
addBranding(slide, theme.background);
if (slideData.notes) slide.addNotes(slideData.notes);
}
function renderBlankSlide(slide, pptx, theme, slideNumber, totalSlides) {
slide.background = { color: theme.background };
addSlideFooter(slide, pptx, theme, slideNumber, totalSlides);
addBranding(slide, theme.background);
}
function addBulletContent(slide, content, theme, startY, maxHeight) {
if (!Array.isArray(content) || content.length === 0) return;
const bulletPoints = content.map((text) => ({
text,
options: {
fontSize: 15,
color: theme.bodyColor,
fontFace: theme.fontBody,
bullet: { code: "25AA", color: theme.bulletColor },
paraSpaceAfter: 10,
},
}));
slide.addText(bulletPoints, {
x: MARGIN_X,
y: startY,
w: CONTENT_W,
h: maxHeight,
valign: "top",
});
}
function addTableContent(slide, pptx, tableData, theme, startY) {
if (!tableData) return;
const rows = [];
if (tableData.headers?.length > 0) {
rows.push(
tableData.headers.map((header) => ({
text: header,
options: {
bold: true,
fontSize: 12,
fontFace: theme.fontBody,
color: theme.tableHeaderColor,
fill: { color: theme.tableHeaderBg },
align: "left",
valign: "middle",
margin: [4, 8, 4, 8],
},
}))
);
}
if (tableData.rows?.length > 0) {
tableData.rows.forEach((row, idx) => {
rows.push(
row.map((cell) => ({
text: cell,
options: {
fontSize: 11,
fontFace: theme.fontBody,
color: theme.bodyColor,
fill: {
color: idx % 2 === 1 ? theme.tableAltRowBg : theme.background,
},
align: "left",
valign: "middle",
margin: [4, 8, 4, 8],
},
}))
);
});
}
if (rows.length === 0) return;
const colCount = rows[0].length;
slide.addTable(rows, {
x: MARGIN_X,
y: startY,
w: CONTENT_W,
colW: CONTENT_W / colCount,
rowH: 0.4,
border: { type: "solid", pt: 0.5, color: theme.tableBorderColor },
});
}
module.exports = {
isDarkColor,
addBranding,
addTopAccentBar,
addAccentUnderline,
addSlideFooter,
renderTitleSlide,
renderSectionSlide,
renderContentSlide,
renderBlankSlide,
addBulletContent,
addTableContent,
};