Files
deerflow-code/frontend-web/src/core/artifacts/download.ts
T

120 lines
3.5 KiB
TypeScript

import { apiFetch } from "../api";
import { downloadMarkdownAsDocx } from "../scheduled-tasks/markdown-docx";
import { getFileName } from "../utils/files";
import { urlOfArtifact } from "./utils";
function filenameFromContentDisposition(header: string | null): string | null {
if (!header) return null;
const star = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(header);
if (star) {
const raw = star[1].replace(/["']/g, "").trim();
try {
return decodeURIComponent(raw);
} catch {
return raw;
}
}
const plain = /filename="?([^";\n]+)"?/i.exec(header);
return plain ? plain[1].trim() : null;
}
async function errorMessageFromResponse(res: Response): Promise<string> {
try {
const data: unknown = await res.json();
if (data && typeof data === "object" && "detail" in data) {
const detail = (data as { detail: unknown }).detail;
if (typeof detail === "string") return detail;
if (
detail &&
typeof detail === "object" &&
"message" in detail &&
typeof (detail as { message: unknown }).message === "string"
) {
return (detail as { message: string }).message;
}
}
} catch {
// ignore
}
return res.statusText || "Request failed";
}
export async function downloadArtifactFile({
filepath,
threadId,
isMock,
publicShareCode,
}: {
filepath: string;
threadId: string;
isMock?: boolean;
publicShareCode?: string;
}): Promise<void> {
const url = urlOfArtifact({ filepath, threadId, isMock, publicShareCode, download: true });
const res = await apiFetch(url);
if (!res.ok) {
throw new Error(await errorMessageFromResponse(res));
}
const cd = res.headers.get("Content-Disposition");
const filename =
filenameFromContentDisposition(cd) ?? getFileName(filepath);
const blob = await res.blob();
const contentType = res.headers.get("Content-Type")?.split(";")[0]?.trim();
const downloadBlob =
contentType && blob.type !== contentType
? new Blob([await blob.arrayBuffer()], { type: contentType })
: blob;
const objectUrl = URL.createObjectURL(downloadBlob);
const a = document.createElement("a");
a.href = objectUrl;
a.download = filename;
a.rel = "noopener";
document.body.appendChild(a);
a.click();
a.remove();
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 30_000);
}
export async function downloadArtifactMarkdownAsWord({
filepath,
threadId,
}: {
filepath: string;
threadId: string;
}): Promise<void> {
const url = urlOfArtifact({ filepath, threadId, download: false });
const res = await apiFetch(url);
if (!res.ok) {
throw new Error(await errorMessageFromResponse(res));
}
const fileName = getFileName(filepath);
const title = fileName.replace(/\.(md|markdown)$/i, "");
await downloadMarkdownAsDocx(await res.text(), fileName, title);
}
export async function openArtifactInNewTab({
filepath,
threadId,
isMock,
publicShareCode,
}: {
filepath: string;
threadId: string;
isMock?: boolean;
publicShareCode?: string;
}): Promise<void> {
const url = urlOfArtifact({ filepath, threadId, isMock, publicShareCode, download: false });
const res = await apiFetch(url);
if (!res.ok) {
throw new Error(await errorMessageFromResponse(res));
}
const blob = await res.blob();
const objectUrl = URL.createObjectURL(blob);
const w = window.open(objectUrl, "_blank", "noopener,noreferrer");
if (w) w.opener = null;
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 120_000);
}