feat: unify Direct Track PDF rendering and simplify export options

Backend changes:
- Apply background image + invisible text layer to all Direct Track PDFs
- Add CHART to regions_to_avoid for text extraction
- Improve visual fidelity for native PDFs and Office documents

Frontend changes:
- Remove JSON, UnifiedDocument, Markdown download buttons
- Simplify to 2-column layout with only Layout PDF and Reflow PDF
- Remove translation JSON download and Layout PDF option
- Keep only Reflow PDF for translated document downloads
- Clean up unused imports (FileJson, Database, FileOutput)

Archives two OpenSpec proposals:
- unify-direct-track-pdf-rendering
- simplify-frontend-export-options

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-12 07:50:43 +08:00
parent 53bfa88773
commit 24253ac15e
15 changed files with 891 additions and 195 deletions

View File

@@ -14,7 +14,6 @@ import {
AlertCircle,
Clock,
Layers,
FileJson,
Loader2,
ArrowLeft,
RefreshCw,
@@ -22,12 +21,10 @@ import {
Table2,
Image,
BarChart3,
Database,
Languages,
Globe,
CheckCircle,
Trash2,
FileOutput
Trash2
} from 'lucide-react'
import type { ProcessingTrack, TranslationStatus, TranslationListItem } from '@/types/apiV2'
import { Badge } from '@/components/ui/badge'
@@ -224,60 +221,6 @@ export default function TaskDetailPage() {
}
}
const handleDownloadMarkdown = async () => {
if (!taskId) return
try {
await apiClientV2.downloadMarkdown(taskId)
toast({
title: t('export.exportSuccess'),
description: 'Markdown 已下載',
variant: 'success',
})
} catch (error: any) {
toast({
title: t('export.exportError'),
description: error.response?.data?.detail || t('errors.networkError'),
variant: 'destructive',
})
}
}
const handleDownloadJSON = async () => {
if (!taskId) return
try {
await apiClientV2.downloadJSON(taskId)
toast({
title: t('export.exportSuccess'),
description: 'JSON 已下載',
variant: 'success',
})
} catch (error: any) {
toast({
title: t('export.exportError'),
description: error.response?.data?.detail || t('errors.networkError'),
variant: 'destructive',
})
}
}
const handleDownloadUnified = async () => {
if (!taskId) return
try {
await apiClientV2.downloadUnified(taskId)
toast({
title: t('export.exportSuccess'),
description: 'UnifiedDocument JSON 已下載',
variant: 'success',
})
} catch (error: any) {
toast({
title: t('export.exportError'),
description: error.response?.data?.detail || t('errors.networkError'),
variant: 'destructive',
})
}
}
const handleStartTranslation = async () => {
if (!taskId || isTranslating) return
@@ -319,24 +262,6 @@ export default function TaskDetailPage() {
}
}
const handleDownloadTranslation = async (lang: string) => {
if (!taskId) return
try {
await apiClientV2.downloadTranslation(taskId, lang)
toast({
title: '下載成功',
description: `翻譯結果 (${lang}) 已下載`,
variant: 'success',
})
} catch (error: any) {
toast({
title: '下載失敗',
description: error.response?.data?.detail || t('errors.networkError'),
variant: 'destructive',
})
}
}
const handleDeleteTranslation = async (lang: string) => {
if (!taskId) return
try {
@@ -542,19 +467,7 @@ export default function TaskDetailPage() {
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-5 gap-3">
<Button onClick={handleDownloadJSON} variant="outline" className="gap-2 h-20 flex-col">
<FileJson className="w-8 h-8" />
<span>JSON</span>
</Button>
<Button onClick={handleDownloadUnified} variant="outline" className="gap-2 h-20 flex-col">
<Database className="w-8 h-8" />
<span></span>
</Button>
<Button onClick={handleDownloadMarkdown} variant="outline" className="gap-2 h-20 flex-col">
<FileText className="w-8 h-8" />
<span>Markdown</span>
</Button>
<div className="grid grid-cols-2 gap-3">
<Button onClick={handleDownloadLayoutPDF} className="gap-2 h-20 flex-col">
<Download className="w-8 h-8" />
<span> PDF</span>
@@ -650,28 +563,12 @@ export default function TaskDetailPage() {
<Button
variant="outline"
size="sm"
onClick={() => handleDownloadTranslation(item.target_lang)}
onClick={() => handleDownloadTranslatedPdf(item.target_lang, 'reflow')}
className="gap-1"
>
<Download className="w-3 h-3" />
JSON
PDF
</Button>
<Select
onValueChange={(format: 'layout' | 'reflow') =>
handleDownloadTranslatedPdf(item.target_lang, format)
}
>
<SelectTrigger className="w-[100px] h-8">
<div className="flex items-center gap-1">
<FileOutput className="w-3 h-3" />
<span className="text-xs">PDF</span>
</div>
</SelectTrigger>
<SelectContent>
<SelectItem value="layout"> PDF</SelectItem>
<SelectItem value="reflow"> PDF</SelectItem>
</SelectContent>
</Select>
<Button
variant="ghost"
size="sm"