feat(changes): PDF render preview for .pdf op targets

This commit is contained in:
lhh010
2026-09-06 23:06:18 +08:00
parent 8456d77a92
commit 6df06717ad
24 changed files with 73 additions and 3 deletions
-1
View File
@@ -20,7 +20,6 @@ import { createElement, type ReactNode } from 'react'
import DOMPurify from 'dompurify'
import { MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives'
import { markdownTextProps } from './markdown-labels.tsx'
import { lazyChunkComponent } from './lazy-chunk.tsx'
import { resolveLocalMediaDest, rewriteLocalImageUrls } from './markdown-images.ts'
import {
analyzeHtmlSegment,
+29
View File
@@ -18,6 +18,7 @@ import { resolveSidebarPath } from '../produced-files.ts'
import { HTML_IFRAME_SANDBOX } from '../html-preview.ts'
import type { SidebarDiffRef, SidebarTab } from '../state.ts'
import { DiffRows, ReadRows } from '../diff/DiffRows.tsx'
import { PdfView } from '../PdfView.tsx'
import { DiffFiles } from '../diff/DiffFiles.tsx'
import { langOfPath } from '../diff/highlight.ts'
import { buildDiffSegments, diffLines, diffStats, parseUnifiedDiff, unifiedSegments, type DiffRow } from '../diff/rows.ts'
@@ -262,6 +263,17 @@ export function DiffPane({ target, scope, height, onHeightCommit, onClose, onExp
return htmlUrl(scope, resolveSidebarPath(scope.cwd, target.path))
}, [htmlOp, scope, target])
// ── PDF render mode: .pdf op targets (read / write / edit, non-error) reuse
// the editor's PdfView verbatim — media-route bytes wrapped into an
// explicitly-typed Blob so the browser's native PDF viewer opens (a
// direct iframe src can fall back to a download). ──────────────────────
const pdfOp = target.kind === 'op' && !target.op.isError && /.pdf$/i.test(target.path)
const [renderingPdf, setRenderingPdf] = useState(false)
const pdfRenderPath = useMemo(() => {
if (!pdfOp || target.kind !== 'op') return ''
return resolveSidebarPath(scope.cwd, target.path)
}, [pdfOp, scope, target])
// Header stats for git targets come off the parsed patch text.
const gitStats = useMemo(() => {
if (target.kind !== 'git' || diffText === null || diffText === '') return null
@@ -405,6 +417,17 @@ export function DiffPane({ target, scope, height, onHeightCommit, onClose, onExp
{t(rendering ? 'changesHtmlRaw' : 'changesHtmlRender')}
</button>
)}
{pdfOp && (
<button
type="button"
className={css.mdToggle}
data-on={renderingPdf ? 'true' : undefined}
aria-pressed={renderingPdf}
onClick={() => { setRenderingPdf(value => !value) }}
>
{t(renderingPdf ? 'changesPdfRaw' : 'changesPdfRender')}
</button>
)}
<button
type="button"
className={css.iconButton}
@@ -417,6 +440,12 @@ export function DiffPane({ target, scope, height, onHeightCommit, onClose, onExp
</div>
{target.kind === 'op' && htmlOp && rendering && htmlRenderSrc !== ''
? <HtmlRenderPreview src={htmlRenderSrc} title={target.path} />
: target.kind === 'op' && pdfOp && renderingPdf && pdfRenderPath !== ''
? (
<div className={css.htmlPane}>
<PdfView scope={scope} path={pdfRenderPath} title={target.path} />
</div>
)
: target.kind === 'op' && mdOp && reading && readingText !== ''
? (
<div className={css.paneBody}>
+2
View File
@@ -23,6 +23,8 @@ export const ar: Record<string, string> = {
changesMdReading: 'قراءة',
changesHtmlRender: "عرض",
changesHtmlRaw: "المصدر",
changesPdfRender: "عرض",
changesPdfRaw: "المصدر",
changesMdRaw: 'الخام',
changesWrite: 'كتابة',
changesEdit: 'تحرير',
+2
View File
@@ -8,6 +8,8 @@ export const de: Record<string, string> = {
changesMdReading: 'Lesen',
changesHtmlRender: "Rendern",
changesHtmlRaw: "Quelltext",
changesPdfRender: "Rendern",
changesPdfRaw: "Quelltext",
changesMdRaw: 'Rohfassung',
changesWrite: 'Schreiben',
changesEdit: 'Bearbeiten',
+2
View File
@@ -15,6 +15,8 @@ export const fr: Record<string, string> = {
changesMdReading: 'Lecture',
changesHtmlRender: "Rendu",
changesHtmlRaw: "Source",
changesPdfRender: "Rendu",
changesPdfRaw: "Source",
changesMdRaw: 'Brut',
changesWrite: 'Écriture',
changesEdit: 'Modification',
+2
View File
@@ -22,6 +22,8 @@ export const hi: Record<string, string> = {
changesMdReading: 'पठन',
changesHtmlRender: "रेंडर",
changesHtmlRaw: "मूल",
changesPdfRender: "रेंडर",
changesPdfRaw: "मूल",
changesMdRaw: 'मूल पाठ',
changesWrite: 'लिखना',
changesEdit: 'संपादन',
+2
View File
@@ -20,6 +20,8 @@ export const id: Record<string, string> = {
changesMdReading: 'Baca',
changesHtmlRender: "Render",
changesHtmlRaw: "Sumber",
changesPdfRender: "Render",
changesPdfRaw: "Sumber",
changesMdRaw: 'Mentah',
changesWrite: 'Tulis',
changesEdit: 'Edit',
+2
View File
@@ -13,6 +13,8 @@ export const it: Record<string, string> = {
changesMdReading: 'Lettura',
changesHtmlRender: "Render",
changesHtmlRaw: "Sorgente",
changesPdfRender: "Render",
changesPdfRaw: "Sorgente",
changesMdRaw: 'Grezzo',
changesWrite: 'Scrittura',
changesEdit: 'Modifica',
+2
View File
@@ -22,6 +22,8 @@ export const ja: Record<string, string> = {
changesMdReading: '閲覧',
changesHtmlRender: "レンダリング",
changesHtmlRaw: "原文",
changesPdfRender: "レンダリング",
changesPdfRaw: "原文",
changesMdRaw: '原文',
changesWrite: '書き込み',
changesEdit: '編集',
+2
View File
@@ -14,6 +14,8 @@ export const ko: Record<string, string> = {
changesMdReading: '읽기',
changesHtmlRender: "렌더링",
changesHtmlRaw: "원문",
changesPdfRender: "렌더링",
changesPdfRaw: "원문",
changesMdRaw: '원문',
changesWrite: '쓰기',
changesEdit: '편집',
+2
View File
@@ -20,6 +20,8 @@ export const nl: Record<string, string> = {
changesMdReading: 'Lezen',
changesHtmlRender: "Renderen",
changesHtmlRaw: "Bron",
changesPdfRender: "Renderen",
changesPdfRaw: "Bron",
changesMdRaw: 'Brontekst',
changesWrite: 'Schrijven',
changesEdit: 'Bewerken',
+2
View File
@@ -24,6 +24,8 @@ export const pl: Record<string, string> = {
changesMdReading: 'Czytanie',
changesHtmlRender: "Renderuj",
changesHtmlRaw: "Źródło",
changesPdfRender: "Renderuj",
changesPdfRaw: "Źródło",
changesMdRaw: 'Źródło',
changesWrite: 'Zapis',
changesEdit: 'Edycja',
+2
View File
@@ -5,6 +5,8 @@ export const pt: Record<string, string> = {
changesMdReading: 'Leitura',
changesHtmlRender: "Renderizar",
changesHtmlRaw: "Fonte",
changesPdfRender: "Renderizar",
changesPdfRaw: "Fonte",
changesMdRaw: 'Bruto',
changesWrite: 'Escrita',
changesEdit: 'Edição',
+2
View File
@@ -18,6 +18,8 @@ export const ru: Record<string, string> = {
changesMdReading: 'Чтение',
changesHtmlRender: "Рендер",
changesHtmlRaw: "Исходник",
changesPdfRender: "Рендер",
changesPdfRaw: "Исходник",
changesMdRaw: 'Исходный текст',
changesWrite: 'Запись',
changesEdit: 'Изменение',
+2
View File
@@ -5,6 +5,8 @@ export const sv: Record<string, string> = {
changesMdReading: 'Läsning',
changesHtmlRender: "Rendera",
changesHtmlRaw: "Källa",
changesPdfRender: "Rendera",
changesPdfRaw: "Källa",
changesMdRaw: 'Råtext',
changesWrite: 'Skriv',
changesEdit: 'Redigera',
+2
View File
@@ -22,6 +22,8 @@ export const th: Record<string, string> = {
changesMdReading: 'อ่าน',
changesHtmlRender: "เรนเดอร์",
changesHtmlRaw: "ต้นฉบับ",
changesPdfRender: "เรนเดอร์",
changesPdfRaw: "ต้นฉบับ",
changesMdRaw: 'ดิบ',
changesWrite: 'เขียน',
changesEdit: 'แก้ไข',
+2
View File
@@ -22,6 +22,8 @@ export const tr: Record<string, string> = {
changesMdReading: 'Okuma',
changesHtmlRender: "Render",
changesHtmlRaw: "Kaynak",
changesPdfRender: "Render",
changesPdfRaw: "Kaynak",
changesMdRaw: 'Ham',
changesWrite: 'Yazma',
changesEdit: 'Düzenleme',
+2
View File
@@ -22,6 +22,8 @@ export const vi: Record<string, string> = {
changesMdReading: 'Đọc',
changesHtmlRender: "Kết xuất",
changesHtmlRaw: "Nguyên văn",
changesPdfRender: "Kết xuất",
changesPdfRaw: "Nguyên văn",
changesMdRaw: 'Gốc',
changesWrite: 'Ghi',
changesEdit: 'Chỉnh sửa',
+2
View File
@@ -37,6 +37,8 @@ export const zhHK: Record<string, string> = {
changesMdReading: '閱讀',
changesHtmlRender: "渲染",
changesHtmlRaw: "原文",
changesPdfRender: "渲染",
changesPdfRaw: "原文",
changesMdRaw: '原文',
changesWrite: '寫入',
changesEdit: '編輯',
+2
View File
@@ -37,6 +37,8 @@ export const zhMO: Record<string, string> = {
changesMdReading: '閱讀',
changesHtmlRender: "渲染",
changesHtmlRaw: "原文",
changesPdfRender: "渲染",
changesPdfRaw: "原文",
changesMdRaw: '原文',
changesWrite: '寫入',
changesEdit: '編輯',
+2
View File
@@ -37,6 +37,8 @@ export const zhTW: Record<string, string> = {
changesMdReading: '閱讀',
changesHtmlRender: "渲染",
changesHtmlRaw: "原文",
changesPdfRender: "渲染",
changesPdfRaw: "原文",
changesMdRaw: '原文',
changesWrite: '寫入',
changesEdit: '編輯',
+4
View File
@@ -30,6 +30,8 @@ export const zh = {
changesMdRaw: '原文',
changesHtmlRender: '渲染',
changesHtmlRaw: '原文',
changesPdfRender: '渲染',
changesPdfRaw: '原文',
changesWrite: '写入',
changesEdit: '编辑',
changesRunning: '执行中',
@@ -475,6 +477,8 @@ export const en: Record<keyof typeof zh, string> = {
changesMdRaw: 'Raw',
changesHtmlRender: 'Render',
changesHtmlRaw: 'Raw',
changesPdfRender: 'Render',
changesPdfRaw: 'Raw',
changesWrite: 'Write',
changesEdit: 'Edit',
changesRunning: 'running',