import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Code2, Download, Eye } from "lucide-react"; import { issuesApi } from "@/api/issues"; import { Button } from "@/components/ui/button"; import { MarkdownBody } from "@/components/MarkdownBody"; import { attachmentDownloadPath, isMarkdownAttachment, isTextAttachment } from "@/lib/issue-attachments"; import { queryKeys } from "@/lib/queryKeys"; export const TEXT_PREVIEW_MAX_BYTES = 512 * 1024; /** Bound the actual response, not just producer-supplied attachment metadata. */ export async function readTextPreview(response: Response) { if (!response.ok) throw new Error(`Could not load file (${response.status}).`); if (!response.body) throw new Error("The file response is empty."); const reader = response.body.getReader(); const decoder = new TextDecoder("utf-8", { fatal: true }); let bytes = 0; let text = ""; try { while (true) { const chunk = await reader.read(); if (chunk.done) break; bytes += chunk.value.byteLength; if (bytes > TEXT_PREVIEW_MAX_BYTES) throw new Error("This file is too large to preview. Download it instead."); text += decoder.decode(chunk.value, { stream: true }); } text += decoder.decode(); if (text.includes("\0")) throw new Error("This file contains binary data. Download it instead."); return text; } finally { await reader.cancel(); reader.releaseLock(); } } export function TextAttachmentPreview({ title, text, markdown, downloadUrl }: { title: string; text: string; markdown: boolean; downloadUrl: string; }) { const [raw, setRaw] = useState(false); return (
File is empty.
: markdown && !raw ?{text}}
Loading file…
; if (attachments.isError) returnFile no longer available. Close this tab or choose another file.
; const downloadUrl = attachmentDownloadPath(attachment); if (!eligible || content.isError) { return ({content.isError ? "Could not preview this file. Retry or download it." : "This file is too large or is not supported for text preview. Download it instead."}
{eligible ? : null}Loading file…
; return