src / main.ts
/**
* LM Studio Plugin - Export to TXT
* Version 1.1.0
*/
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 sanitizeText(text: string): string {
return text
.replace(/\r/g, "")
.trim();
}
function buildTxtContent(messages: ChatMessage[]): string {
let content = "";
content += "=".repeat(70) + "\n";
content += "EXPORTACION DE CHAT - LM STUDIO\n";
content += "Fecha: " + new Date().toLocaleString("es-ES") + "\n";
content += "Mensajes: " + messages.length + "\n";
content += "=".repeat(70) + "\n\n";
messages.forEach((msg, index) => {
const time = msg.timestamp
? new Date(msg.timestamp).toLocaleTimeString("es-ES")
: "--:--";
const role =
msg.role === "user"
? "USUARIO"
: msg.role === "assistant"
? "ASISTENTE"
: "SISTEMA";
content += "-".repeat(50) + "\n";
content += `#${index + 1} | [${time}] ${role}\n`;
content += "-".repeat(50) + "\n";
content += sanitizeText(msg.content) + "\n\n";
});
content += "=".repeat(70) + "\n";
content += "Generado por Export to TXT Plugin\n";
content += "=".repeat(70) + "\n";
return content;
}
async function exportToTxt(context: PluginContext) {
try {
const messages = context.getChatMessages();
if (!messages || messages.length === 0) {
context.showToast("No hay mensajes para exportar", "info");
return;
}
const content = buildTxtContent(messages);
const now = new Date();
const safeDate = now.toISOString().replace(/[:.]/g, "-");
const filePath = await context.showSaveFileDialog({
filters: [
{
name: "Archivo TXT",
extensions: ["txt"],
},
],
defaultName: `chat-export-${safeDate}.txt`,
});
if (!filePath) {
context.showToast("Exportación cancelada", "info");
return;
}
const fileHandle = await (window as any).showSaveFilePicker({
suggestedName: filePath.split("/").pop() || "chat-export.txt",
types: [
{
description: "Archivo de texto",
accept: {
"text/plain": [".txt"],
},
},
],
});
const writable = await fileHandle.createWritable();
await writable.write(content);
await writable.close();
context.showToast("Chat exportado correctamente", "success");
} catch (error: any) {
console.error(error);
context.showToast(
"Error al exportar: " + (error?.message || "Error desconocido"),
"error"
);
}
}
export async function onLoad(context: PluginContext) {
console.log("Plugin Export to TXT cargado");
context.registerCommand({
id: "export-chat-to-txt",
title: "Exportar Chat a TXT",
description: "Exporta toda la conversación actual a un archivo TXT",
handler: () => exportToTxt(context),
});
context.registerChatBarButton({
id: "export-txt-button",
label: "Export TXT",
icon: "📄",
handler: () => exportToTxt(context),
});
}