Add Claw report PDF export
This commit is contained in:
parent
05387d08f3
commit
57f4d08c31
@ -261,6 +261,34 @@
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll {
|
||||
scrollbar-gutter: stable;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(148, 163, 184, 0.42) rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
.chat-card-x-scroll::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll::-webkit-scrollbar-track {
|
||||
background: rgba(148, 163, 184, 0.14);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll::-webkit-scrollbar-thumb {
|
||||
background: rgba(148, 163, 184, 0.42);
|
||||
border: 2px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(148, 163, 184, 0.58);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.step {
|
||||
counter-increment: step;
|
||||
}
|
||||
|
||||
@ -959,6 +959,11 @@ type CanvasTypewriterTextProps = {
|
||||
className?: string;
|
||||
renderMarkdown?: boolean;
|
||||
preserveMarkdownTables?: boolean;
|
||||
enablePdfDownload?: boolean;
|
||||
pdfFileName?: string;
|
||||
pdfSiteLogo?: string | null;
|
||||
pdfSiteName?: string | null;
|
||||
pdfTitle?: string;
|
||||
onActiveChange?: (typingKey: string, active: boolean) => void;
|
||||
onComplete?: (typingKey: string) => void;
|
||||
};
|
||||
@ -1077,6 +1082,11 @@ function CanvasTypewriterText({
|
||||
className,
|
||||
renderMarkdown = false,
|
||||
preserveMarkdownTables = false,
|
||||
enablePdfDownload = false,
|
||||
pdfFileName,
|
||||
pdfSiteLogo,
|
||||
pdfSiteName,
|
||||
pdfTitle,
|
||||
onActiveChange,
|
||||
onComplete,
|
||||
}: CanvasTypewriterTextProps) {
|
||||
@ -1131,7 +1141,16 @@ function CanvasTypewriterText({
|
||||
if (renderMarkdown) {
|
||||
return (
|
||||
<div className="w-full min-w-0">
|
||||
<Response className={className}>{displayText}</Response>
|
||||
<Response
|
||||
className={className}
|
||||
enablePdfDownload={enablePdfDownload}
|
||||
pdfFileName={pdfFileName}
|
||||
pdfSiteLogo={pdfSiteLogo}
|
||||
pdfSiteName={pdfSiteName}
|
||||
pdfTitle={pdfTitle}
|
||||
>
|
||||
{displayText}
|
||||
</Response>
|
||||
{showCursor ? <CanvasTypewriterCursor /> : null}
|
||||
</div>
|
||||
);
|
||||
@ -2995,6 +3014,9 @@ export function CanvasChatPanel({
|
||||
|
||||
// 获取网站设置(包含广告横幅配置和画布设置)
|
||||
const { data: siteSettings } = useSWR<{
|
||||
siteName?: string;
|
||||
siteLogo?: string | null;
|
||||
siteLogoDark?: string | null;
|
||||
bannerEnabled?: boolean;
|
||||
bannerText?: string;
|
||||
bannerLink?: string | null;
|
||||
@ -11178,7 +11200,7 @@ export function CanvasChatPanel({
|
||||
"flex min-w-0 flex-col gap-2",
|
||||
message.role === 'user'
|
||||
? "max-w-[88%] items-end"
|
||||
: "w-full max-w-[88%] items-start"
|
||||
: "w-full max-w-full items-start"
|
||||
)}>
|
||||
{message.role === 'user' ? (
|
||||
<div
|
||||
@ -11398,6 +11420,11 @@ export function CanvasChatPanel({
|
||||
className="break-words [overflow-wrap:anywhere]"
|
||||
renderMarkdown
|
||||
preserveMarkdownTables={shouldTypeClawAssistantMessage}
|
||||
enablePdfDownload={message.role === "assistant" && !shouldAnimateAnalysis}
|
||||
pdfFileName={`Claw分析报告-${message.id}`}
|
||||
pdfSiteLogo={siteSettings?.siteLogo || siteSettings?.siteLogoDark || null}
|
||||
pdfSiteName={siteSettings?.siteName || "AI Chatbot"}
|
||||
pdfTitle="Claw 分析报告"
|
||||
onActiveChange={handleTypewriterActiveChange}
|
||||
onComplete={handleTypewriterComplete}
|
||||
/>
|
||||
@ -11415,6 +11442,11 @@ export function CanvasChatPanel({
|
||||
className="break-words [overflow-wrap:anywhere]"
|
||||
renderMarkdown
|
||||
preserveMarkdownTables={shouldTypeClawAssistantMessage}
|
||||
enablePdfDownload={message.role === "assistant" && !shouldAnimatePlainText}
|
||||
pdfFileName={`Claw分析报告-${message.id}`}
|
||||
pdfSiteLogo={siteSettings?.siteLogo || siteSettings?.siteLogoDark || null}
|
||||
pdfSiteName={siteSettings?.siteName || "AI Chatbot"}
|
||||
pdfTitle="Claw 分析报告"
|
||||
onActiveChange={handleTypewriterActiveChange}
|
||||
onComplete={handleTypewriterComplete}
|
||||
/>
|
||||
|
||||
@ -306,6 +306,7 @@ export function OpenClawChartCard({ chart }: { chart: OpenClawChartSpec }) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDownload}
|
||||
data-pdf-exclude="true"
|
||||
className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-background hover:text-foreground"
|
||||
title="下载图表数据 CSV"
|
||||
>
|
||||
@ -313,9 +314,9 @@ export function OpenClawChartCard({ chart }: { chart: OpenClawChartSpec }) {
|
||||
<span>下载数据</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-x-auto px-3 py-3">
|
||||
<div className="chat-card-x-scroll overflow-x-auto px-3 pb-4 pt-3">
|
||||
<svg
|
||||
className="min-w-[620px] max-w-full"
|
||||
className="min-w-[720px] max-w-none"
|
||||
role="img"
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
>
|
||||
|
||||
308
components/elements/response-pdf-download.tsx
Normal file
308
components/elements/response-pdf-download.tsx
Normal file
@ -0,0 +1,308 @@
|
||||
"use client";
|
||||
|
||||
import { FileDownIcon } from "lucide-react";
|
||||
import { useState, type RefObject } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type ResponsePdfDownloadButtonProps = {
|
||||
contentRef: RefObject<HTMLElement | null>;
|
||||
fileName?: string;
|
||||
siteLogo?: string | null;
|
||||
siteName?: string | null;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
function escapeHtml(value: string) {
|
||||
return value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function getSafeTitle(value?: string | null) {
|
||||
return String(value || "").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function getSafeFileName(value?: string | null) {
|
||||
const normalized = getSafeTitle(value || "Claw 分析报告")
|
||||
.replace(/[\\/:*?"<>|]/g, "_")
|
||||
.slice(0, 80);
|
||||
return normalized || "Claw 分析报告";
|
||||
}
|
||||
|
||||
function clonePrintableStyles(targetWindow: Window) {
|
||||
document
|
||||
.querySelectorAll<HTMLLinkElement | HTMLStyleElement>(
|
||||
'link[rel="stylesheet"], style'
|
||||
)
|
||||
.forEach((node) => {
|
||||
targetWindow.document.head.appendChild(node.cloneNode(true));
|
||||
});
|
||||
}
|
||||
|
||||
function createPrintOverrides(targetWindow: Window) {
|
||||
const style = targetWindow.document.createElement("style");
|
||||
style.textContent = `
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #111827;
|
||||
--border: #e5e7eb;
|
||||
--card: #ffffff;
|
||||
--muted: #f3f4f6;
|
||||
--muted-foreground: #6b7280;
|
||||
--primary: #111827;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
background: #ffffff;
|
||||
color: #111827;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
|
||||
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
|
||||
.claw-pdf-shell {
|
||||
box-sizing: border-box;
|
||||
width: 1120px;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 32px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.claw-pdf-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding-bottom: 18px;
|
||||
margin-bottom: 24px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.claw-pdf-brand {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.claw-pdf-logo,
|
||||
.claw-pdf-logo-fallback {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
flex: 0 0 auto;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.claw-pdf-logo {
|
||||
object-fit: contain;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.claw-pdf-logo-fallback {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #111827;
|
||||
color: #ffffff;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.claw-pdf-site {
|
||||
font-size: 18px;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.claw-pdf-title {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.claw-pdf-time {
|
||||
flex: 0 0 auto;
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.claw-pdf-content {
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
[data-pdf-exclude="true"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll {
|
||||
overflow: visible !important;
|
||||
padding-bottom: 0 !important;
|
||||
scrollbar-width: none !important;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
table {
|
||||
page-break-inside: auto;
|
||||
}
|
||||
|
||||
tr,
|
||||
svg,
|
||||
img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
svg {
|
||||
max-width: 100% !important;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4 landscape;
|
||||
margin: 12mm;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.claw-pdf-shell {
|
||||
width: auto;
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
targetWindow.document.head.appendChild(style);
|
||||
}
|
||||
|
||||
export function ResponsePdfDownloadButton({
|
||||
contentRef,
|
||||
fileName,
|
||||
siteLogo,
|
||||
siteName,
|
||||
title = "Claw 分析报告",
|
||||
}: ResponsePdfDownloadButtonProps) {
|
||||
const [isPreparing, setIsPreparing] = useState(false);
|
||||
const resolvedSiteName = getSafeTitle(siteName) || "AI Chatbot";
|
||||
const resolvedTitle = getSafeTitle(title) || "Claw 分析报告";
|
||||
|
||||
const handleDownloadPdf = () => {
|
||||
const contentElement = contentRef.current;
|
||||
if (!contentElement) {
|
||||
toast.error("未找到可导出的内容");
|
||||
return;
|
||||
}
|
||||
|
||||
const printWindow = window.open(
|
||||
"",
|
||||
"_blank",
|
||||
"width=1200,height=820"
|
||||
);
|
||||
if (!printWindow) {
|
||||
toast.error("浏览器阻止了 PDF 导出窗口,请允许弹窗后重试");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsPreparing(true);
|
||||
|
||||
try {
|
||||
const safeDocumentTitle = getSafeFileName(fileName || resolvedTitle);
|
||||
printWindow.document.open();
|
||||
printWindow.document.write(
|
||||
`<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(
|
||||
safeDocumentTitle
|
||||
)}</title><base href="${escapeHtml(window.location.origin)}"></head><body></body></html>`
|
||||
);
|
||||
printWindow.document.close();
|
||||
clonePrintableStyles(printWindow);
|
||||
createPrintOverrides(printWindow);
|
||||
|
||||
const shell = printWindow.document.createElement("main");
|
||||
shell.className = "claw-pdf-shell";
|
||||
|
||||
const header = printWindow.document.createElement("header");
|
||||
header.className = "claw-pdf-header";
|
||||
const brand = printWindow.document.createElement("div");
|
||||
brand.className = "claw-pdf-brand";
|
||||
if (siteLogo) {
|
||||
const logo = printWindow.document.createElement("img");
|
||||
logo.className = "claw-pdf-logo";
|
||||
logo.alt = resolvedSiteName;
|
||||
logo.src = siteLogo;
|
||||
brand.appendChild(logo);
|
||||
} else {
|
||||
const fallback = printWindow.document.createElement("span");
|
||||
fallback.className = "claw-pdf-logo-fallback";
|
||||
fallback.textContent = resolvedSiteName.slice(0, 1).toUpperCase();
|
||||
brand.appendChild(fallback);
|
||||
}
|
||||
|
||||
const brandText = printWindow.document.createElement("div");
|
||||
brandText.innerHTML = `<div class="claw-pdf-site">${escapeHtml(
|
||||
resolvedSiteName
|
||||
)}</div><div class="claw-pdf-title">${escapeHtml(resolvedTitle)}</div>`;
|
||||
brand.appendChild(brandText);
|
||||
|
||||
const time = printWindow.document.createElement("div");
|
||||
time.className = "claw-pdf-time";
|
||||
time.textContent = new Date().toLocaleString("zh-CN", {
|
||||
hour12: false,
|
||||
});
|
||||
|
||||
header.appendChild(brand);
|
||||
header.appendChild(time);
|
||||
shell.appendChild(header);
|
||||
|
||||
const content = printWindow.document.createElement("section");
|
||||
content.className = "claw-pdf-content";
|
||||
const clonedContent = contentElement.cloneNode(true) as HTMLElement;
|
||||
clonedContent
|
||||
.querySelectorAll('[data-pdf-exclude="true"]')
|
||||
.forEach((node) => node.remove());
|
||||
content.appendChild(clonedContent);
|
||||
shell.appendChild(content);
|
||||
printWindow.document.body.appendChild(shell);
|
||||
|
||||
window.setTimeout(() => {
|
||||
setIsPreparing(false);
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
}, 600);
|
||||
} catch (error) {
|
||||
setIsPreparing(false);
|
||||
printWindow.close();
|
||||
console.error("[Response PDF] export failed:", error);
|
||||
toast.error("PDF 导出失败,请稍后重试");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex h-8 items-center gap-1.5 rounded-md border border-border/70 bg-background/80 px-2.5 text-xs font-medium text-muted-foreground shadow-sm transition-colors hover:bg-muted hover:text-foreground",
|
||||
isPreparing && "pointer-events-none opacity-70"
|
||||
)}
|
||||
onClick={handleDownloadPdf}
|
||||
disabled={isPreparing}
|
||||
title="导出整段回复为 PDF"
|
||||
>
|
||||
<FileDownIcon className="h-3.5 w-3.5" />
|
||||
<span>{isPreparing ? "准备中" : "下载 PDF"}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@ -9,6 +9,7 @@ import {
|
||||
OpenClawChartCard,
|
||||
splitOpenClawChartBlocks,
|
||||
} from "@/components/elements/openclaw-chart-card";
|
||||
import { ResponsePdfDownloadButton } from "@/components/elements/response-pdf-download";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
@ -19,6 +20,11 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
type ResponseProps = ComponentProps<typeof Streamdown> & {
|
||||
collapsibleImageAnalysis?: boolean;
|
||||
enablePdfDownload?: boolean;
|
||||
pdfFileName?: string;
|
||||
pdfSiteLogo?: string | null;
|
||||
pdfSiteName?: string | null;
|
||||
pdfTitle?: string;
|
||||
onImageEdit?: (imageUrl: string) => void;
|
||||
onDrawingComplete?: (annotatedImageUrl: string) => void;
|
||||
onImageEnhance?: (imageUrl: string) => void;
|
||||
@ -59,6 +65,19 @@ type DownloadableMarkdownTableProps = ComponentProps<"table"> & {
|
||||
node?: unknown;
|
||||
};
|
||||
|
||||
const OPENCLAW_CHART_BLOCK_REGEX =
|
||||
/```(?:openclaw-chart|openclaw_chart|claw-chart|claw_chart|chart)\s*\n[\s\S]*?```/i;
|
||||
const MARKDOWN_TABLE_REGEX =
|
||||
/(^|\n)\s*\|[^\n|]+(?:\|[^\n|]+)+\|\s*\n\s*\|(?:\s*:?-{3,}:?\s*\|)+/;
|
||||
|
||||
export function hasDownloadableReportContent(content: string) {
|
||||
const normalized = String(content || "");
|
||||
return (
|
||||
OPENCLAW_CHART_BLOCK_REGEX.test(normalized) ||
|
||||
MARKDOWN_TABLE_REGEX.test(normalized)
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeTableCellText(value: string) {
|
||||
return value.replace(/\s+/g, " ").trim();
|
||||
}
|
||||
@ -295,10 +314,14 @@ function DownloadableMarkdownTable({
|
||||
|
||||
return (
|
||||
<div className="my-3 max-w-full overflow-hidden rounded-xl border border-border bg-background/70 whitespace-normal">
|
||||
<div className="flex items-center justify-end border-b border-border/70 bg-muted/40 px-2 py-1.5">
|
||||
<div
|
||||
className="flex items-center justify-end border-b border-border/70 bg-muted/40 px-2 py-1.5"
|
||||
data-pdf-exclude="true"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDownload}
|
||||
data-pdf-exclude="true"
|
||||
className="inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-background hover:text-foreground"
|
||||
title="下载表格 CSV"
|
||||
>
|
||||
@ -306,13 +329,14 @@ function DownloadableMarkdownTable({
|
||||
<span>下载表格</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<div className="chat-card-x-scroll max-w-full overflow-x-auto overscroll-x-contain pb-2">
|
||||
<table
|
||||
ref={tableRef}
|
||||
className={cn(
|
||||
"w-max min-w-full border-collapse text-sm",
|
||||
"[&_td]:min-w-32 [&_td]:align-top [&_td]:break-words [&_td]:whitespace-normal",
|
||||
"[&_th]:min-w-32 [&_th]:whitespace-nowrap",
|
||||
"[&_td]:min-w-[12rem] [&_td]:align-top [&_td]:break-words [&_td]:whitespace-normal",
|
||||
"[&_td:first-child]:min-w-[22rem] [&_th:first-child]:min-w-[22rem]",
|
||||
"[&_th]:min-w-[12rem] [&_th]:whitespace-nowrap",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@ -329,6 +353,11 @@ function DownloadableMarkdownTable({
|
||||
export const Response = ({
|
||||
className,
|
||||
collapsibleImageAnalysis = false,
|
||||
enablePdfDownload = false,
|
||||
pdfFileName,
|
||||
pdfSiteLogo,
|
||||
pdfSiteName,
|
||||
pdfTitle,
|
||||
onImageEdit,
|
||||
onDrawingComplete,
|
||||
onImageEnhance,
|
||||
@ -343,6 +372,7 @@ export const Response = ({
|
||||
isProcessingWatermarkRemove,
|
||||
...props
|
||||
}: ResponseProps) => {
|
||||
const pdfContentRef = useRef<HTMLDivElement>(null);
|
||||
const streamdownClassName = cn(
|
||||
"w-full min-w-0 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0 [&_code]:break-words [&_pre]:max-w-full [&_pre]:overflow-x-auto",
|
||||
className,
|
||||
@ -478,13 +508,37 @@ export const Response = ({
|
||||
typeof props.children === "string"
|
||||
? normalizeMarkdownContent(props.children)
|
||||
: null;
|
||||
const shouldShowPdfDownload =
|
||||
Boolean(enablePdfDownload && rawText && hasDownloadableReportContent(rawText));
|
||||
const wrapPdfDownload = (content: ReactNode) => {
|
||||
if (!shouldShowPdfDownload) return content;
|
||||
|
||||
return (
|
||||
<div className="w-full min-w-0">
|
||||
<div
|
||||
className="not-prose mb-2 flex justify-end"
|
||||
data-pdf-exclude="true"
|
||||
>
|
||||
<ResponsePdfDownloadButton
|
||||
contentRef={pdfContentRef}
|
||||
fileName={pdfFileName}
|
||||
siteLogo={pdfSiteLogo}
|
||||
siteName={pdfSiteName}
|
||||
title={pdfTitle}
|
||||
/>
|
||||
</div>
|
||||
<div ref={pdfContentRef}>{content}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (rawText) {
|
||||
const analysisSection = collapsibleImageAnalysis
|
||||
? extractImageAnalysisSection(rawText)
|
||||
: null;
|
||||
|
||||
if (analysisSection) {
|
||||
return (
|
||||
return wrapPdfDownload(
|
||||
<div className={streamdownClassName}>
|
||||
{renderTextWithImages(analysisSection.before, "analysis-before")}
|
||||
<ImageAnalysisCollapsible
|
||||
@ -497,7 +551,9 @@ export const Response = ({
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={streamdownClassName}>{renderTextWithImages(rawText, "raw")}</div>;
|
||||
return wrapPdfDownload(
|
||||
<div className={streamdownClassName}>{renderTextWithImages(rawText, "raw")}</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user