diff --git a/README.md b/README.md index a58ed23..21056a2 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ - **🌐 内嵌浏览器**:多开网页 tab,后退 / 前进 / 刷新;内容运行在沙箱 iframe;外链默认按协议分流——HTTP 在侧边栏打开、HTTPS 走系统浏览器(设置页可分别调整) - **💻 真实终端**:xterm.js + node-pty 真实 shell,断线重连回放;可选为模型注入 `terminal_*` 工具 - **📂 模型侧边栏打开(可选)**:全局设置开启后注入 `sidebar_open` 工具——模型可主动在侧边栏打开文件 / 文件夹(树以该目录为根)/ HTTP(S) 网页 -- **🌿 文件变动**:Git 视角(真 diff / 历史 / 暂存·提交·还原 / worktree·子仓库选择)与本轮文件视角(模型读 / 写 / 编辑实时追踪,按文件分组、按类型筛选)**双视角合一**;统一 diff 渲染(改蓝配对 + 行内字符级高亮 + 语法着色(含 mjs/cjs/mts/cts、CSS/SCSS/Less、HTML/XML/SVG/Vue、GraphQL、JSONC/JSON5)+ 上下文折叠),底部可拖拽预览面板,可一键展开为独立 diff tab(默认自由浮窗,可在设置改为面板下半 split);`.md` 操作(读 / 写 / 编辑)预览头部可切换**阅读模式**——经共享 MarkdownText 渲染 GFM 表格 / 任务列表 / 删除线 / 脚注 / 数学公式,本地图片自动改写为 `/sidebar/file` 媒体路由;含 ```mermaid 围栏时走编辑器同款懒加载 mermaid 渲染器(图可点击缩放 / 平移);**敏感内容脱敏**——凭据形态路径整文件遮罩、普通文件按内容形态遮值(api_key: / Bearer / sk- / AKIA / ghp_ / PEM 等,字段名保留),默认开启、预览面板一键开关(localStorage 记忆),仅影响显示、不改会话数据。已知边界:mermaid 无引号节点标签含被遮密钥时,图回退源码(规避:标签加引号);`.html` 操作(读 / 写 / 编辑)预览头部可切换**渲染模式**——复用编辑器同款 `/sidebar/html` 路由 iframe,相对资源(./style.css、img/x.png)同路由解析,分段读取也渲染完整文档,恒定沙箱(opaque origin + CSP 头,无逃生门) +- **🌿 文件变动**:Git 视角(真 diff / 历史 / 暂存·提交·还原 / worktree·子仓库选择)与本轮文件视角(模型读 / 写 / 编辑实时追踪,按文件分组、按类型筛选)**双视角合一**;统一 diff 渲染(改蓝配对 + 行内字符级高亮 + 语法着色(含 mjs/cjs/mts/cts、CSS/SCSS/Less、HTML/XML/SVG/Vue、GraphQL、JSONC/JSON5)+ 上下文折叠),底部可拖拽预览面板,可一键展开为独立 diff tab(默认自由浮窗,可在设置改为面板下半 split);`.md` 操作(读 / 写 / 编辑)预览头部可切换**阅读模式**——经共享 MarkdownText 渲染 GFM 表格 / 任务列表 / 删除线 / 脚注 / 数学公式,本地图片自动改写为 `/sidebar/file` 媒体路由;含 ```mermaid 围栏时走编辑器同款懒加载 mermaid 渲染器(图可点击缩放 / 平移);**敏感内容脱敏**——凭据形态路径整文件遮罩、普通文件按内容形态遮值(api_key: / Bearer / sk- / AKIA / ghp_ / PEM 等,字段名保留),默认开启、预览面板一键开关(localStorage 记忆),仅影响显示、不改会话数据。已知边界:mermaid 无引号节点标签含被遮密钥时,图回退源码(规避:标签加引号);`.html` 操作(读 / 写 / 编辑)预览头部可切换**渲染模式**——复用编辑器同款 `/sidebar/html` 路由 iframe,相对资源(./style.css、img/x.png)同路由解析,分段读取也渲染完整文档,恒定沙箱(opaque origin + CSP 头,无逃生门);`.pdf` 操作(读 / 写 / 编辑)同样可切换**渲染模式**——复用编辑器同款 PDF 预览(媒体路由字节流 + 显式 Blob,浏览器原生查看器内嵌,附下载入口) - **🧩 后台任务页**:subagent 拓扑 + 后台任务(退出码 / 实时输出 / 强制终止) - **💬 侧边对话(beta)**:Codex 风格的侧边线程——继承主会话完整上下文(含进行中的回合与工具调用)独立运行,不进入主会话;线程内可持续追问,一键「保存为新会话」提升为顶层会话 - **🪟 双工作台**:右侧栏 + 底部面板;拖 Tab 拆分 / 合并分栏(可跨面板),移动端自动合并全宽抽屉 diff --git a/README_EN.md b/README_EN.md index 23053a8..171d812 100644 --- a/README_EN.md +++ b/README_EN.md @@ -44,7 +44,7 @@ - **🌐 Embedded Browser**: multiple web tabs with back / forward / refresh; content runs in a sandboxed iframe; external links are routed by protocol by default — HTTP opens in the sidebar, HTTPS goes to the system browser (both adjustable in settings) - **💻 Real Terminal**: xterm.js + node-pty real shell, reconnect with transcript replay; optionally injects `terminal_*` tools for the model - **📂 Model-driven sidebar opens (opt-in)**: with the global setting on, the `sidebar_open` tool lets the model actively open files / folders (tree rooted there) / HTTP(S) pages in the sidebar -- **🌿 Changes**: one tab, two lenses — **Git** (real diff / history / stage·commit·revert / worktree & child-repo selection) and **This Session** (live tracking of every file the model reads / writes / edits, grouped by file with kind filters); a unified diff renderer (mod pairing + intra-line character highlights + syntax coloring incl. mjs/cjs/mts/cts, CSS/SCSS/Less, HTML/XML/SVG/Vue, GraphQL, JSONC/JSON5 + context folding), a draggable bottom preview pane, and one-click expansion into a dedicated diff tab; `.md` ops (read / write / edit) offer a **reading-mode** toggle in the preview header — the shared MarkdownText renders GFM tables / task lists / strikethrough / footnotes / math, with local images rewritten through the /sidebar/file media route; documents with ```mermaid fences render through the editor's lazy mermaid renderer (click-to-zoom / pan diagrams); **secret redaction** — credential-shaped paths mask whole files and ordinary files mask secret-shaped values (api_key: / Bearer / sk- / AKIA / ghp_ / PEM …, field names kept), on by default with a one-click preview-pane toggle (persisted in localStorage), display-only — session data untouched. Known edge: an unquoted mermaid label containing a redacted secret breaks the diagram (falls back to source); workaround: quote the label. `.html` ops (read / write / edit) gain a **Render** toggle in the preview head — the editor's `/sidebar/html` route iframe, with relative assets resolving inside the route and segmented reads rendering the full document; always sandboxed (opaque origin + CSP header, no escape hatch) +- **🌿 Changes**: one tab, two lenses — **Git** (real diff / history / stage·commit·revert / worktree & child-repo selection) and **This Session** (live tracking of every file the model reads / writes / edits, grouped by file with kind filters); a unified diff renderer (mod pairing + intra-line character highlights + syntax coloring incl. mjs/cjs/mts/cts, CSS/SCSS/Less, HTML/XML/SVG/Vue, GraphQL, JSONC/JSON5 + context folding), a draggable bottom preview pane, and one-click expansion into a dedicated diff tab; `.md` ops (read / write / edit) offer a **reading-mode** toggle in the preview header — the shared MarkdownText renders GFM tables / task lists / strikethrough / footnotes / math, with local images rewritten through the /sidebar/file media route; documents with ```mermaid fences render through the editor's lazy mermaid renderer (click-to-zoom / pan diagrams); **secret redaction** — credential-shaped paths mask whole files and ordinary files mask secret-shaped values (api_key: / Bearer / sk- / AKIA / ghp_ / PEM …, field names kept), on by default with a one-click preview-pane toggle (persisted in localStorage), display-only — session data untouched. Known edge: an unquoted mermaid label containing a redacted secret breaks the diagram (falls back to source); workaround: quote the label. `.html` ops (read / write / edit) gain a **Render** toggle in the preview head — the editor's `/sidebar/html` route iframe, with relative assets resolving inside the route and segmented reads rendering the full document; always sandboxed (opaque origin + CSP header, no escape hatch). `.pdf` ops (read / write / edit) gain the same **Render** toggle — the editor's PDF preview reused verbatim (media-route bytes + explicit Blob, the browser's native viewer inline, with a download fallback) - **🧩 Background Tasks**: agent topology + background tasks (exit codes / live output / force-kill) - **💬 Side Chat (beta)**: Codex-style side threads — the child inherits the parent's FULL context (completed turns + the pending question + the in-progress turn's assistant output and tool activity, honestly frozen as "interrupted") and runs independently without entering the main conversation; threads support continuous follow-ups (auto-resumed after a DSH restart) and one-click "Save as new session" promotion to a top-level session - **🪟 Dual Workbench**: right sidebar + bottom panel; drag tabs to split / merge panes (cross-panel), mobile auto-merges into a full-width drawer diff --git a/src/client/MarkdownHtml.tsx b/src/client/MarkdownHtml.tsx index 6d3852a..de06633 100644 --- a/src/client/MarkdownHtml.tsx +++ b/src/client/MarkdownHtml.tsx @@ -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, diff --git a/src/client/changes/DiffPane.tsx b/src/client/changes/DiffPane.tsx index 2eed59a..4dff973 100644 --- a/src/client/changes/DiffPane.tsx +++ b/src/client/changes/DiffPane.tsx @@ -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')} )} + {pdfOp && ( + { setRenderingPdf(value => !value) }} + > + {t(renderingPdf ? 'changesPdfRaw' : 'changesPdfRender')} + + )} {target.kind === 'op' && htmlOp && rendering && htmlRenderSrc !== '' ? + : target.kind === 'op' && pdfOp && renderingPdf && pdfRenderPath !== '' + ? ( + + + + ) : target.kind === 'op' && mdOp && reading && readingText !== '' ? ( diff --git a/src/client/locales-ar.ts b/src/client/locales-ar.ts index a8caf69..90c20b5 100644 --- a/src/client/locales-ar.ts +++ b/src/client/locales-ar.ts @@ -23,6 +23,8 @@ export const ar: Record = { changesMdReading: 'قراءة', changesHtmlRender: "عرض", changesHtmlRaw: "المصدر", + changesPdfRender: "عرض", + changesPdfRaw: "المصدر", changesMdRaw: 'الخام', changesWrite: 'كتابة', changesEdit: 'تحرير', diff --git a/src/client/locales-de.ts b/src/client/locales-de.ts index 0ba7288..799d1b7 100644 --- a/src/client/locales-de.ts +++ b/src/client/locales-de.ts @@ -8,6 +8,8 @@ export const de: Record = { changesMdReading: 'Lesen', changesHtmlRender: "Rendern", changesHtmlRaw: "Quelltext", + changesPdfRender: "Rendern", + changesPdfRaw: "Quelltext", changesMdRaw: 'Rohfassung', changesWrite: 'Schreiben', changesEdit: 'Bearbeiten', diff --git a/src/client/locales-fr.ts b/src/client/locales-fr.ts index 6ab0219..0f9b757 100644 --- a/src/client/locales-fr.ts +++ b/src/client/locales-fr.ts @@ -15,6 +15,8 @@ export const fr: Record = { changesMdReading: 'Lecture', changesHtmlRender: "Rendu", changesHtmlRaw: "Source", + changesPdfRender: "Rendu", + changesPdfRaw: "Source", changesMdRaw: 'Brut', changesWrite: 'Écriture', changesEdit: 'Modification', diff --git a/src/client/locales-hi.ts b/src/client/locales-hi.ts index bccd8ee..02f932b 100644 --- a/src/client/locales-hi.ts +++ b/src/client/locales-hi.ts @@ -22,6 +22,8 @@ export const hi: Record = { changesMdReading: 'पठन', changesHtmlRender: "रेंडर", changesHtmlRaw: "मूल", + changesPdfRender: "रेंडर", + changesPdfRaw: "मूल", changesMdRaw: 'मूल पाठ', changesWrite: 'लिखना', changesEdit: 'संपादन', diff --git a/src/client/locales-id.ts b/src/client/locales-id.ts index fc77f22..796bf0c 100644 --- a/src/client/locales-id.ts +++ b/src/client/locales-id.ts @@ -20,6 +20,8 @@ export const id: Record = { changesMdReading: 'Baca', changesHtmlRender: "Render", changesHtmlRaw: "Sumber", + changesPdfRender: "Render", + changesPdfRaw: "Sumber", changesMdRaw: 'Mentah', changesWrite: 'Tulis', changesEdit: 'Edit', diff --git a/src/client/locales-it.ts b/src/client/locales-it.ts index 6f35e68..59385cb 100644 --- a/src/client/locales-it.ts +++ b/src/client/locales-it.ts @@ -13,6 +13,8 @@ export const it: Record = { changesMdReading: 'Lettura', changesHtmlRender: "Render", changesHtmlRaw: "Sorgente", + changesPdfRender: "Render", + changesPdfRaw: "Sorgente", changesMdRaw: 'Grezzo', changesWrite: 'Scrittura', changesEdit: 'Modifica', diff --git a/src/client/locales-ja.ts b/src/client/locales-ja.ts index a13ac86..aca6e71 100644 --- a/src/client/locales-ja.ts +++ b/src/client/locales-ja.ts @@ -22,6 +22,8 @@ export const ja: Record = { changesMdReading: '閲覧', changesHtmlRender: "レンダリング", changesHtmlRaw: "原文", + changesPdfRender: "レンダリング", + changesPdfRaw: "原文", changesMdRaw: '原文', changesWrite: '書き込み', changesEdit: '編集', diff --git a/src/client/locales-ko.ts b/src/client/locales-ko.ts index aa63627..42b5733 100644 --- a/src/client/locales-ko.ts +++ b/src/client/locales-ko.ts @@ -14,6 +14,8 @@ export const ko: Record = { changesMdReading: '읽기', changesHtmlRender: "렌더링", changesHtmlRaw: "원문", + changesPdfRender: "렌더링", + changesPdfRaw: "원문", changesMdRaw: '원문', changesWrite: '쓰기', changesEdit: '편집', diff --git a/src/client/locales-nl.ts b/src/client/locales-nl.ts index 368b36e..589029c 100644 --- a/src/client/locales-nl.ts +++ b/src/client/locales-nl.ts @@ -20,6 +20,8 @@ export const nl: Record = { changesMdReading: 'Lezen', changesHtmlRender: "Renderen", changesHtmlRaw: "Bron", + changesPdfRender: "Renderen", + changesPdfRaw: "Bron", changesMdRaw: 'Brontekst', changesWrite: 'Schrijven', changesEdit: 'Bewerken', diff --git a/src/client/locales-pl.ts b/src/client/locales-pl.ts index 0595a01..2138899 100644 --- a/src/client/locales-pl.ts +++ b/src/client/locales-pl.ts @@ -24,6 +24,8 @@ export const pl: Record = { changesMdReading: 'Czytanie', changesHtmlRender: "Renderuj", changesHtmlRaw: "Źródło", + changesPdfRender: "Renderuj", + changesPdfRaw: "Źródło", changesMdRaw: 'Źródło', changesWrite: 'Zapis', changesEdit: 'Edycja', diff --git a/src/client/locales-pt.ts b/src/client/locales-pt.ts index 0341fc0..1417abe 100644 --- a/src/client/locales-pt.ts +++ b/src/client/locales-pt.ts @@ -5,6 +5,8 @@ export const pt: Record = { changesMdReading: 'Leitura', changesHtmlRender: "Renderizar", changesHtmlRaw: "Fonte", + changesPdfRender: "Renderizar", + changesPdfRaw: "Fonte", changesMdRaw: 'Bruto', changesWrite: 'Escrita', changesEdit: 'Edição', diff --git a/src/client/locales-ru.ts b/src/client/locales-ru.ts index c73b796..e1d01cf 100644 --- a/src/client/locales-ru.ts +++ b/src/client/locales-ru.ts @@ -18,6 +18,8 @@ export const ru: Record = { changesMdReading: 'Чтение', changesHtmlRender: "Рендер", changesHtmlRaw: "Исходник", + changesPdfRender: "Рендер", + changesPdfRaw: "Исходник", changesMdRaw: 'Исходный текст', changesWrite: 'Запись', changesEdit: 'Изменение', diff --git a/src/client/locales-sv.ts b/src/client/locales-sv.ts index c5e23e5..c57f2fd 100644 --- a/src/client/locales-sv.ts +++ b/src/client/locales-sv.ts @@ -5,6 +5,8 @@ export const sv: Record = { changesMdReading: 'Läsning', changesHtmlRender: "Rendera", changesHtmlRaw: "Källa", + changesPdfRender: "Rendera", + changesPdfRaw: "Källa", changesMdRaw: 'Råtext', changesWrite: 'Skriv', changesEdit: 'Redigera', diff --git a/src/client/locales-th.ts b/src/client/locales-th.ts index d042378..9bf3462 100644 --- a/src/client/locales-th.ts +++ b/src/client/locales-th.ts @@ -22,6 +22,8 @@ export const th: Record = { changesMdReading: 'อ่าน', changesHtmlRender: "เรนเดอร์", changesHtmlRaw: "ต้นฉบับ", + changesPdfRender: "เรนเดอร์", + changesPdfRaw: "ต้นฉบับ", changesMdRaw: 'ดิบ', changesWrite: 'เขียน', changesEdit: 'แก้ไข', diff --git a/src/client/locales-tr.ts b/src/client/locales-tr.ts index df1cd44..e18ee3c 100644 --- a/src/client/locales-tr.ts +++ b/src/client/locales-tr.ts @@ -22,6 +22,8 @@ export const tr: Record = { changesMdReading: 'Okuma', changesHtmlRender: "Render", changesHtmlRaw: "Kaynak", + changesPdfRender: "Render", + changesPdfRaw: "Kaynak", changesMdRaw: 'Ham', changesWrite: 'Yazma', changesEdit: 'Düzenleme', diff --git a/src/client/locales-vi.ts b/src/client/locales-vi.ts index fc79a0e..42b803e 100644 --- a/src/client/locales-vi.ts +++ b/src/client/locales-vi.ts @@ -22,6 +22,8 @@ export const vi: Record = { 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', diff --git a/src/client/locales-zh-HK.ts b/src/client/locales-zh-HK.ts index 1055f8c..9e9af6a 100644 --- a/src/client/locales-zh-HK.ts +++ b/src/client/locales-zh-HK.ts @@ -37,6 +37,8 @@ export const zhHK: Record = { changesMdReading: '閱讀', changesHtmlRender: "渲染", changesHtmlRaw: "原文", + changesPdfRender: "渲染", + changesPdfRaw: "原文", changesMdRaw: '原文', changesWrite: '寫入', changesEdit: '編輯', diff --git a/src/client/locales-zh-MO.ts b/src/client/locales-zh-MO.ts index a1fc72a..1d17d13 100644 --- a/src/client/locales-zh-MO.ts +++ b/src/client/locales-zh-MO.ts @@ -37,6 +37,8 @@ export const zhMO: Record = { changesMdReading: '閱讀', changesHtmlRender: "渲染", changesHtmlRaw: "原文", + changesPdfRender: "渲染", + changesPdfRaw: "原文", changesMdRaw: '原文', changesWrite: '寫入', changesEdit: '編輯', diff --git a/src/client/locales-zh-TW.ts b/src/client/locales-zh-TW.ts index f9cb3de..235624d 100644 --- a/src/client/locales-zh-TW.ts +++ b/src/client/locales-zh-TW.ts @@ -37,6 +37,8 @@ export const zhTW: Record = { changesMdReading: '閱讀', changesHtmlRender: "渲染", changesHtmlRaw: "原文", + changesPdfRender: "渲染", + changesPdfRaw: "原文", changesMdRaw: '原文', changesWrite: '寫入', changesEdit: '編輯', diff --git a/src/client/locales.ts b/src/client/locales.ts index db9b062..7a3f92b 100644 --- a/src/client/locales.ts +++ b/src/client/locales.ts @@ -30,6 +30,8 @@ export const zh = { changesMdRaw: '原文', changesHtmlRender: '渲染', changesHtmlRaw: '原文', + changesPdfRender: '渲染', + changesPdfRaw: '原文', changesWrite: '写入', changesEdit: '编辑', changesRunning: '执行中', @@ -475,6 +477,8 @@ export const en: Record = { changesMdRaw: 'Raw', changesHtmlRender: 'Render', changesHtmlRaw: 'Raw', + changesPdfRender: 'Render', + changesPdfRaw: 'Raw', changesWrite: 'Write', changesEdit: 'Edit', changesRunning: 'running',