feat: replace pymupdf image extraction with pdf2image + Heron layout detection
PDF image extraction now renders pages with pdf2image, detects figure regions using docling-layout-heron (RT-DETRv2), and crops only the detected pictures. Removes full-page fallback for text-only pages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,7 @@ import {
|
||||
Settings
|
||||
} from "lucide-react";
|
||||
import { formatFileSize, formatDate } from "@/lib/utils";
|
||||
import { knowledgeBaseAPI } from "@/lib/api";
|
||||
import { knowledgeBaseAPI, resolveImageUrl } from "@/lib/api";
|
||||
import { KnowledgeBaseDetail, Document } from "@/types";
|
||||
|
||||
export default function KnowledgeBaseDetailPage() {
|
||||
@@ -158,13 +158,12 @@ export default function KnowledgeBaseDetailPage() {
|
||||
const handleViewDocument = async (doc: Document) => {
|
||||
try {
|
||||
const token = localStorage.getItem("auth_token");
|
||||
const res = await fetch(`/api/knowledge-bases/documents/${doc.id}/download`, {
|
||||
const res = await fetch(resolveImageUrl(`/knowledge-bases/documents/${doc.id}/download`), {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error("下载失败");
|
||||
if (!res.ok) throw new Error("获取文档失败");
|
||||
const blob = await res.blob();
|
||||
|
||||
// 从 Content-Disposition 提取文件名,或用 doc 信息拼接
|
||||
const disposition = res.headers.get("Content-Disposition");
|
||||
let filename = `${doc.title}${doc.file_type}`;
|
||||
if (disposition) {
|
||||
@@ -172,15 +171,11 @@ export default function KnowledgeBaseDetailPage() {
|
||||
if (match) filename = decodeURIComponent(match[1].replace(/["']/g, ""));
|
||||
}
|
||||
|
||||
// 触发浏览器下载
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
window.open(url, '_blank');
|
||||
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
||||
} catch {
|
||||
window.open(`/api/knowledge-bases/documents/${doc.id}/download`, '_blank');
|
||||
window.open(resolveImageUrl(`/knowledge-bases/documents/${doc.id}/download`), '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -113,7 +113,18 @@ export default function MessageItem({ message, selectedModel }: MessageItemProps
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(message.content);
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(message.content);
|
||||
} else {
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.value = message.content;
|
||||
textarea.style.position = "fixed";
|
||||
textarea.style.left = "-9999px";
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
toast.success("已复制");
|
||||
} catch {
|
||||
toast.error("复制失败");
|
||||
|
||||
@@ -339,6 +339,15 @@ export const useChatStore = create<ChatStore>((set, get) => ({
|
||||
scheduleFlush();
|
||||
},
|
||||
(_sessionId: number, messageId?: number, userMessageId?: number) => {
|
||||
// 刷出缓冲区中剩余的内容
|
||||
if (rafId !== null) {
|
||||
cancelAnimationFrame(rafId);
|
||||
rafId = null;
|
||||
}
|
||||
if (contentBuffer) {
|
||||
flushBuffer();
|
||||
}
|
||||
|
||||
if (messageId) {
|
||||
set((state) => ({
|
||||
messages: state.messages.map(msg => {
|
||||
|
||||
Reference in New Issue
Block a user