128 lines
6.0 KiB
JavaScript
128 lines
6.0 KiB
JavaScript
import {download, multipart, post} from "./api.js";
|
|
|
|
export function createWorkspaceTransfer({conversation, getChatById, setNotice, promptPopup, confirmPopup, setChatLoading, loadChatSessions}) {
|
|
async function exportCurrentChat() {
|
|
const chat = getChatById(conversation.activeChatId);
|
|
if (!chat || !conversation.currentMessages.length) return;
|
|
const format = await promptPopup({
|
|
eyebrow: "Export",
|
|
title: "Export format",
|
|
description: "Use txt, md, or json.",
|
|
label: "Format",
|
|
value: "md",
|
|
placeholder: "md",
|
|
confirmLabel: "Download"
|
|
});
|
|
if (format === null) return;
|
|
const normalized = ["txt", "md", "json"].includes(String(format || "").trim().toLowerCase())
|
|
? String(format).trim().toLowerCase()
|
|
: "md";
|
|
const response = await download(`/exports/chats/${chat.id}?format=${normalized}`);
|
|
if (response.error) return setNotice(response.error, "error");
|
|
const {blob} = response;
|
|
const url = URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.href = url;
|
|
link.download = `${(chat.title.replace(/[^a-z0-9-_]+/gi, "-").replace(/^-+|-+$/g, "") || "chat")}.${normalized}`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
link.remove();
|
|
URL.revokeObjectURL(url);
|
|
setNotice(`Chat exported as ${normalized}.`, "success");
|
|
}
|
|
|
|
async function backupWorkspace() {
|
|
const response = await download("/exports/workspace/archive");
|
|
if (response.error) return setNotice(response.error, "error");
|
|
const {blob} = response;
|
|
const url = URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.href = url;
|
|
link.download = `workspace-backup-${new Date().toISOString().slice(0, 10)}.tar.gz`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
link.remove();
|
|
URL.revokeObjectURL(url);
|
|
setNotice("Workspace archive downloaded with images.", "success");
|
|
}
|
|
|
|
function formatImportPreview(preview, warnings = []) {
|
|
const counts = [
|
|
`${preview.personas || 0} persona${preview.personas === 1 ? "" : "s"}`,
|
|
`${preview.chats || 0} chat${preview.chats === 1 ? "" : "s"}`,
|
|
`${preview.messages || 0} message${preview.messages === 1 ? "" : "s"}`,
|
|
`${preview.templates || 0} template${preview.templates === 1 ? "" : "s"}`
|
|
].join(", ");
|
|
const warningText = warnings.length ? ` Warnings: ${warnings.slice(0, 4).join("; ")}${warnings.length > 4 ? "; ..." : ""}` : "";
|
|
return `Import preview: ${counts}.${warningText}`;
|
|
}
|
|
|
|
async function importWorkspaceFromFile(file) {
|
|
if (!file) return;
|
|
setChatLoading(true, "Reading workspace backup...");
|
|
const isArchive = file.name.toLowerCase().endsWith(".tar.gz");
|
|
if (isArchive) {
|
|
const submit = async (path, allowDuplicates = false) => {
|
|
const body = new FormData();
|
|
body.append("archive", file);
|
|
if (allowDuplicates) body.append("allowDuplicates", "true");
|
|
return multipart(path, body);
|
|
};
|
|
try {
|
|
const preview = await submit("/imports/workspace/archive/preview");
|
|
setChatLoading(false);
|
|
if (preview.error || preview.ok === false) return setNotice((preview.errors || []).slice(0, 4).join("; ") || preview.error || "Invalid archive.", "error");
|
|
const confirmed = await confirmPopup({eyebrow: "Workspace import", title: "Review import",
|
|
description: formatImportPreview(preview.preview || {}, preview.warnings || []), confirmLabel: "Import"});
|
|
if (!confirmed) return;
|
|
setChatLoading(true, "Importing workspace and images...");
|
|
const result = await submit("/imports/workspace/archive", Boolean(preview.preview?.duplicates?.length));
|
|
if (result.error) return setNotice(result.error, "error");
|
|
await loadChatSessions();
|
|
setNotice(`Workspace imported: ${result.imported.chats} chats with their images.`, "success");
|
|
} catch { setNotice("Workspace archive could not be imported.", "error"); }
|
|
finally { setChatLoading(false); }
|
|
return;
|
|
}
|
|
let workspace;
|
|
try {
|
|
workspace = JSON.parse(await file.text());
|
|
} catch {
|
|
setChatLoading(false);
|
|
return setNotice("Import failed: choose a valid JSON workspace backup.", "error");
|
|
}
|
|
|
|
const preview = await post("/imports/workspace/preview", { workspace });
|
|
if (preview.error || preview.ok === false) {
|
|
setChatLoading(false);
|
|
const detail = (preview.errors || []).slice(0, 4).join("; ");
|
|
return setNotice(detail || preview.error || "Workspace import validation failed.", "error");
|
|
}
|
|
|
|
setChatLoading(false);
|
|
const confirmed = await confirmPopup({
|
|
eyebrow: "Workspace import",
|
|
title: "Review import",
|
|
description: formatImportPreview(preview.preview || {}, preview.warnings || []),
|
|
confirmLabel: "Import"
|
|
});
|
|
if (!confirmed) return;
|
|
|
|
setChatLoading(true, "Importing workspace...");
|
|
const imported = await post("/imports/workspace", {
|
|
workspace,
|
|
allowDuplicates: Boolean(preview.preview?.duplicates?.length)
|
|
});
|
|
if (imported.error) {
|
|
setChatLoading(false);
|
|
const detail = (imported.errors || imported.warnings || []).slice(0, 4).join("; ");
|
|
return setNotice(detail || imported.error, "error");
|
|
}
|
|
await loadChatSessions();
|
|
setChatLoading(false);
|
|
setNotice(`Workspace imported: ${imported.imported.personas} personas, ${imported.imported.chats} chats, ${imported.imported.templates} templates.`, "success");
|
|
}
|
|
|
|
return {exportCurrentChat, backupWorkspace, importWorkspaceFromFile};
|
|
}
|