src / main.ts
import jsPDF from "jspdf";
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 exportToPdf(context: PluginContext) {
try {
const messages = context.getChatMessages();
if (!messages || messages.length === 0) {
context.showToast("No hay mensajes para exportar", "info");
return;
}
const now = new Date();
const fileName = `chat-export-${now
.toISOString()
.replace(/[:.]/g, "-")}.pdf`;
const filePath = await context.showSaveFileDialog({
filters: [
{
name: "Archivo PDF",
extensions: ["pdf"],
},
],
defaultName: fileName,
});
if (!filePath) {
context.showToast("Exportación cancelada", "info");
return;
}
const doc = new jsPDF();
let y = 10;
doc.setFontSize(18);
doc.text("EXPORTACION DE CHAT - LM STUDIO", 10, y);
y += 10;
doc.setFontSize(10);
doc.text(
"Fecha: " + new Date().toLocaleString("es-ES"),
10,
y
);
y += 10;
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")
: "--:--";
const header = `#${index + 1} [${time}] ${role}`;
doc.setFontSize(12);
doc.text(header, 10, y);
y += 7;
doc.setFontSize(10);
const lines = doc.splitTextToSize(
normalizeText(msg.content),
180
);
doc.text(lines, 10, y);
y += lines.length * 6 + 8;
if (y > 260) {
doc.addPage();
y = 10;
}
});
const pdfBlob = doc.output("blob");
const handle = await (window as any).showSaveFilePicker({
suggestedName: fileName,
types: [
{
description: "Archivo PDF",
accept: {
"application/pdf": [".pdf"],
},
},
],
});
const writable = await handle.createWritable();
await writable.write(pdfBlob);
await writable.close();
context.showToast("PDF exportado correctamente", "success");
} catch (error: any) {
console.error(error);
context.showToast(
"Error al exportar PDF: " +
(error?.message || "Error desconocido"),
"error"
);
}
}
export async function onLoad(context: PluginContext) {
console.log("Plugin Export to PDF cargado");
context.registerCommand({
id: "export-chat-to-pdf",
title: "Exportar Chat a PDF",
description: "Exporta toda la conversación actual a PDF",
handler: () => exportToPdf(context),
});
context.registerChatBarButton({
id: "export-pdf-button",
label: "Export PDF",
icon: "📕",
handler: () => exportToPdf(context),
});
}