src / main.ts
import {
Document,
Packer,
Paragraph,
TextRun,
HeadingLevel
} from "docx";
interface ChatMessage {
role: "user" | "assistant" | "system";
content: string;
timestamp?: number;
}
interface SaveDialogOptions {
filters: Array<{
name: string;
extensions: string[];
}>;
defaultName: string;
}
interface Command {
id: string;
title: string;
description: string;
handler: () => void;
}
interface ChatBarButton {
id: string;
label: string;
icon: string;
handler: () => void;
}
interface PluginContext {
getChatMessages: () => ChatMessage[];
showSaveFileDialog: (options: SaveDialogOptions) => Promise<string | null>;
showToast: (
message: string,
type?: "success" | "error" | "info"
) => void;
registerCommand: (cmd: Command) => void;
registerChatBarButton: (btn: ChatBarButton) => void;
}
function normalizeText(text: string): string {
return text.replace(/\r/g, "").trim();
}
async function exportToWord(context: PluginContext) {
try {
const messages = context.getChatMessages();
if (!messages || messages.length === 0) {
context.showToast("No hay mensajes para exportar", "info");
return;
}
const paragraphs: Paragraph[] = [];
paragraphs.push(
new Paragraph({
text: "EXPORTACION DE CHAT - LM STUDIO",
heading: HeadingLevel.TITLE
})
);
paragraphs.push(
new Paragraph({
children: [
new TextRun({
text:
"Fecha: " +
new Date().toLocaleString("es-ES"),
italics: true
})
]
})
);
paragraphs.push(new Paragraph(" "));
messages.forEach((msg, index) => {
const role =
msg.role === "user"
? "USUARIO"
: msg.role === "assistant"
? "ASISTENTE"
: "SISTEMA";
const time = msg.timestamp
? new Date(msg.timestamp).toLocaleTimeString("es-ES")
: "--:--";
paragraphs.push(
new Paragraph({
children: [
new TextRun({
text: `#${index + 1} [${time}] ${role}`,
bold: true
})
]
})
);
paragraphs.push(
new Paragraph({
children: [
new TextRun({
text: normalizeText(msg.content)
})
]
})
);
paragraphs.push(new Paragraph(" "));
});
const doc = new Document({
sections: [
{
properties: {},
children: paragraphs
}
]
});
const blob = await Packer.toBlob(doc);
const now = new Date();
const fileName =
"chat-export-" +
now.toISOString().replace(/[:.]/g, "-") +
".docx";
await context.showSaveFileDialog({
filters: [
{
name: "Documento Word",
extensions: ["docx"]
}
],
defaultName: fileName
});
const handle = await (window as any).showSaveFilePicker({
suggestedName: fileName,
types: [
{
description: "Documento Word",
accept: {
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
[".docx"]
}
}
]
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
context.showToast(
"Documento Word exportado correctamente",
"success"
);
} catch (error: any) {
console.error(error);
context.showToast(
"Error exportando Word: " +
(error?.message || "Error desconocido"),
"error"
);
}
}
export async function onLoad(context: PluginContext) {
console.log("Plugin Export to Word cargado");
context.registerCommand({
id: "export-chat-to-word",
title: "Exportar Chat a Word",
description: "Exporta toda la conversación actual a DOCX",
handler: () => exportToWord(context)
});
context.registerChatBarButton({
id: "export-word-button",
label: "Export Word",
icon: "📘",
handler: () => exportToWord(context)
});
}