mirror of
https://github.com/Mintplex-Labs/anything-llm
synced 2026-04-25 17:15:37 +02:00
* 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
302 lines
12 KiB
JavaScript
302 lines
12 KiB
JavaScript
const {
|
|
getDeploymentVersion,
|
|
} = require("../../../../../../endpoints/utils.js");
|
|
const createFilesLib = require("../lib.js");
|
|
const {
|
|
getTheme,
|
|
getMargins,
|
|
loadLibraries,
|
|
htmlToDocxElements,
|
|
createCoverPageSection,
|
|
createRunningHeader,
|
|
createRunningFooter,
|
|
DEFAULT_NUMBERING_CONFIG,
|
|
} = require("./utils.js");
|
|
|
|
module.exports.CreateDocxFile = {
|
|
name: "create-docx-file",
|
|
plugin: function () {
|
|
return {
|
|
name: "create-docx-file",
|
|
setup(aibitat) {
|
|
aibitat.function({
|
|
super: aibitat,
|
|
name: this.name,
|
|
description:
|
|
"Create a Microsoft Word document (.docx) from markdown or plain text content. Supports professional styling with color themes, title pages, and running headers/footers.",
|
|
examples: [
|
|
{
|
|
prompt: "Create a Word document with meeting notes",
|
|
call: JSON.stringify({
|
|
filename: "meeting-notes.docx",
|
|
content:
|
|
"# Meeting Notes - Q1 Planning\n\n## Attendees\n- John Smith\n- Sarah Johnson\n- Mike Chen\n\n## Agenda\n1. Review Q4 results\n2. Set Q1 goals\n3. Assign tasks\n\n## Action Items\n| Person | Task | Due Date |\n|--------|------|----------|\n| John | Prepare budget report | Jan 15 |\n| Sarah | Draft marketing plan | Jan 20 |\n| Mike | Schedule follow-up | Jan 10 |",
|
|
}),
|
|
},
|
|
{
|
|
prompt:
|
|
"Create a professional project proposal with a title page",
|
|
call: JSON.stringify({
|
|
filename: "project-proposal.docx",
|
|
title: "Project Alpha Proposal",
|
|
subtitle: "Strategic Initiative for Q2 2024",
|
|
author: "Product Team",
|
|
theme: "blue",
|
|
includeTitlePage: true,
|
|
content:
|
|
"## Executive Summary\nThis proposal outlines the development of **Project Alpha**, a next-generation platform.\n\n## Objectives\n- Increase efficiency by 40%\n- Reduce costs by $50,000 annually\n- Improve user satisfaction\n\n## Timeline\n| Phase | Duration | Deliverables |\n|-------|----------|-------------|\n| Phase 1 | 4 weeks | Requirements |\n| Phase 2 | 8 weeks | Development |\n| Phase 3 | 2 weeks | Testing |\n\n## Budget\nTotal estimated budget: **$150,000**",
|
|
}),
|
|
},
|
|
{
|
|
prompt: "Create technical documentation with warm theme",
|
|
call: JSON.stringify({
|
|
filename: "api-documentation.docx",
|
|
title: "API Documentation",
|
|
theme: "warm",
|
|
margins: "narrow",
|
|
content:
|
|
"# API Documentation\n\n## Authentication\nAll API requests require a Bearer token in the Authorization header.\n\n```javascript\nconst headers = {\n 'Authorization': 'Bearer YOUR_TOKEN',\n 'Content-Type': 'application/json'\n};\n```\n\n## Endpoints\n\n### GET /users\nReturns a list of all users.\n\n### POST /users\nCreates a new user.\n\n> **Note:** Rate limiting applies to all endpoints.",
|
|
}),
|
|
},
|
|
],
|
|
parameters: {
|
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
type: "object",
|
|
properties: {
|
|
filename: {
|
|
type: "string",
|
|
description:
|
|
"The filename for the Word document. Will automatically add .docx extension if not present.",
|
|
},
|
|
title: {
|
|
type: "string",
|
|
description:
|
|
"Document title for metadata and title page. If not provided, will be extracted from content or use filename.",
|
|
},
|
|
subtitle: {
|
|
type: "string",
|
|
description:
|
|
"Optional subtitle displayed on the title page below the main title.",
|
|
},
|
|
author: {
|
|
type: "string",
|
|
description:
|
|
"Optional author name displayed on the title page.",
|
|
},
|
|
content: {
|
|
type: "string",
|
|
description:
|
|
"The content to convert to a Word document. Fully supports markdown formatting.",
|
|
},
|
|
theme: {
|
|
type: "string",
|
|
enum: ["neutral", "blue", "warm"],
|
|
description:
|
|
"Color theme for the document. 'neutral' (slate/grey), 'blue' (corporate blue), or 'warm' (earthy tones). Defaults to neutral.",
|
|
},
|
|
margins: {
|
|
type: "string",
|
|
enum: ["normal", "narrow", "wide"],
|
|
description:
|
|
"Page margin preset. 'normal' (standard), 'narrow' (data-heavy docs), or 'wide' (letters/memos). Defaults to normal.",
|
|
},
|
|
includeTitlePage: {
|
|
type: "boolean",
|
|
description:
|
|
"Include a professional title page with centered title, subtitle, author, and date. Content starts on page 2 with running headers/footers.",
|
|
},
|
|
},
|
|
required: ["filename", "content"],
|
|
additionalProperties: false,
|
|
},
|
|
handler: async function ({
|
|
filename = "document.docx",
|
|
title = null,
|
|
subtitle = null,
|
|
author = null,
|
|
content = "",
|
|
theme = "neutral",
|
|
margins = "normal",
|
|
includeTitlePage = false,
|
|
}) {
|
|
try {
|
|
this.super.handlerProps.log(`Using the create-docx-file tool.`);
|
|
|
|
const hasExtension = /\.docx$/i.test(filename);
|
|
if (!hasExtension) filename = `${filename}.docx`;
|
|
const displayFilename = filename.split("/").pop();
|
|
|
|
const documentTitle =
|
|
title ||
|
|
content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
displayFilename.replace(/\.docx$/i, "");
|
|
|
|
if (this.super.requestToolApproval) {
|
|
const approval = await this.super.requestToolApproval({
|
|
skillName: this.name,
|
|
payload: { filename: displayFilename, title: documentTitle },
|
|
description: `Create Word document "${displayFilename}"`,
|
|
});
|
|
if (!approval.approved) {
|
|
this.super.introspect(
|
|
`${this.caller}: User rejected the ${this.name} request.`
|
|
);
|
|
return approval.message;
|
|
}
|
|
}
|
|
|
|
this.super.introspect(
|
|
`${this.caller}: Creating Word document "${displayFilename}"${includeTitlePage ? " with title page" : ""}`
|
|
);
|
|
|
|
const libs = await loadLibraries();
|
|
const { marked, docx } = libs;
|
|
const { Document, Packer, Paragraph, TextRun } = docx;
|
|
marked.setOptions({
|
|
gfm: true,
|
|
breaks: true,
|
|
});
|
|
|
|
const themeColors = getTheme(theme);
|
|
const marginConfig = getMargins(margins);
|
|
|
|
const html = marked.parse(content);
|
|
this.super.handlerProps.log(
|
|
`create-docx-file: Parsed markdown to HTML (${html.length} chars), theme: ${theme}, margins: ${margins}`
|
|
);
|
|
|
|
const logoBuffer = createFilesLib.getLogo({
|
|
forDarkBackground: false,
|
|
format: "buffer",
|
|
});
|
|
|
|
const docElements = await htmlToDocxElements(
|
|
html,
|
|
libs,
|
|
this.super.handlerProps.log,
|
|
themeColors
|
|
);
|
|
|
|
if (docElements.length === 0) {
|
|
docElements.push(
|
|
new Paragraph({
|
|
children: [new TextRun({ text: content })],
|
|
})
|
|
);
|
|
}
|
|
|
|
const sections = [];
|
|
|
|
if (includeTitlePage) {
|
|
const currentDate = new Date().toLocaleDateString("en-US", {
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
});
|
|
|
|
sections.push(
|
|
createCoverPageSection(docx, {
|
|
title: documentTitle,
|
|
subtitle,
|
|
author,
|
|
date: currentDate,
|
|
theme: themeColors,
|
|
margins: marginConfig,
|
|
logoBuffer,
|
|
})
|
|
);
|
|
|
|
sections.push({
|
|
properties: {
|
|
page: {
|
|
margin: marginConfig,
|
|
},
|
|
},
|
|
children: docElements,
|
|
headers: {
|
|
default: createRunningHeader(
|
|
docx,
|
|
documentTitle,
|
|
themeColors
|
|
),
|
|
},
|
|
footers: {
|
|
default: createRunningFooter(docx, logoBuffer, themeColors),
|
|
},
|
|
});
|
|
} else {
|
|
sections.push({
|
|
properties: {
|
|
page: {
|
|
margin: marginConfig,
|
|
},
|
|
},
|
|
children: docElements,
|
|
footers: {
|
|
default: createRunningFooter(docx, logoBuffer, themeColors),
|
|
},
|
|
});
|
|
}
|
|
|
|
const doc = new Document({
|
|
title: documentTitle,
|
|
creator: `AnythingLLM ${getDeploymentVersion()}`,
|
|
description: `Word Document generated by AnythingLLM ${getDeploymentVersion()}`,
|
|
numbering: DEFAULT_NUMBERING_CONFIG,
|
|
sections,
|
|
});
|
|
|
|
const buffer = await Packer.toBuffer(doc);
|
|
const bufferSizeKB = (buffer.length / 1024).toFixed(2);
|
|
|
|
this.super.handlerProps.log(
|
|
`create-docx-file: Generated buffer - size: ${bufferSizeKB}KB, title: "${documentTitle}", theme: ${theme}`
|
|
);
|
|
|
|
const savedFile = await createFilesLib.saveGeneratedFile({
|
|
fileType: "docx",
|
|
extension: "docx",
|
|
buffer,
|
|
displayFilename,
|
|
});
|
|
|
|
this.super.socket.send("fileDownloadCard", {
|
|
filename: savedFile.displayFilename,
|
|
storageFilename: savedFile.filename,
|
|
fileSize: savedFile.fileSize,
|
|
});
|
|
|
|
createFilesLib.registerOutput(this.super, "DocxFileDownload", {
|
|
filename: savedFile.displayFilename,
|
|
storageFilename: savedFile.filename,
|
|
fileSize: savedFile.fileSize,
|
|
});
|
|
|
|
this.super.introspect(
|
|
`${this.caller}: Successfully created Word document "${displayFilename}"`
|
|
);
|
|
|
|
const styleInfo = [
|
|
theme !== "neutral" ? `${theme} theme` : null,
|
|
margins !== "normal" ? `${margins} margins` : null,
|
|
includeTitlePage ? "title page" : null,
|
|
].filter(Boolean);
|
|
|
|
const styleDesc =
|
|
styleInfo.length > 0 ? ` with ${styleInfo.join(", ")}` : "";
|
|
|
|
return `Successfully created Word document "${displayFilename}" (${bufferSizeKB}KB)${styleDesc}. The document includes formatted content with tables, images, Page X of Y footer, and professional styling.`;
|
|
} catch (e) {
|
|
this.super.handlerProps.log(
|
|
`create-docx-file error: ${e.message}`
|
|
);
|
|
this.super.introspect(`Error: ${e.message}`);
|
|
return `Error creating Word document: ${e.message}`;
|
|
}
|
|
},
|
|
});
|
|
},
|
|
};
|
|
},
|
|
};
|