diff --git a/pr_body.md b/pr_body.md new file mode 100644 index 0000000..31758d8 --- /dev/null +++ b/pr_body.md @@ -0,0 +1,65 @@ +# 标签页右键菜单增强 + PDF 导出样式系统 + +## 背景与动机 +本 PR 解决两个用户反馈的问题,并顺带做了若干增强: +- **Issue 1**:标签页右键菜单只有「打开文件所在文件夹」,缺少关闭 / 刷新 / 关闭其他 / 关闭全部等常用操作。 +- **Issue 2**:PDF 导出无法自定义字体与颜色。参考 md-to.com 的样式能力,新增独立设置页、24 套预设模板、紧凑 / 宽松间距与导出前实时预览。 + +## 一、标签页右键菜单增强 +`TabBar.vue` 新增右键菜单项: +- 关闭 / 关闭其他(仅剩 1 个标签时禁用)/ 关闭全部(无标签时禁用)/ 刷新 +- 复制路径(写入剪贴板,带成功 / 失败提示) +- 打开文件所在文件夹 +`App.vue` 接入对应事件处理,新增 `copyPath()`(`navigator.clipboard` + `execCommand` 兜底)。 + +## 二、PDF 导出样式系统 +### 独立设置标签页 +设置对话框新增「PDF 导出」独立标签页(`settingsTab` 切换),与阅读设置分离,更清晰。 + +### 24 套预设模板 +分为三组: +- **文档(doc)**:默认 / 极简 / 学术 / 护眼 / 商务蓝 / 经典棕 / GitHub +- **开发(dev)**:暗色 / 终端绿 / Dracula / VS Code 蓝 / Nord / Solarized 亮 / Solarized 暗 / Gruvbox 亮 / Gruvbox 暗 / Monokai / Catppuccin / One Dark +- **创意(creative)**:樱花粉 / 薰衣草 / 海洋青 / 日落橙 / 森林绿 + +### 间距变体(紧凑 / 标准 / 宽松) +对应 md-to.com 的 Compact / Standard / Loose。通过 `PdfDensity` + `PDF_DENSITY_SPACING`(lineHeight / blockGap)驱动,导出的 HTML 用 `calc(var(--pdf-block-gap,1em)*N)` 实现段落间距。 + +### 分组下拉 +模板下拉按「文档 / 开发 / 创意」用 `` 分组,便于浏览。 + +### 实时预览 +新增 `usePdfPreview.ts`,复用真实导出管线(`renderMarkdown` + `buildExportHtml`)在隔离 ` + +
+
{{ t("pdfStyle.dark") }}
+ +
+ + + + +
+ +
- -
- - -
- -
-
-
- {{ t("settings.fileAssociation") }} +
+ +
-
- {{ t("settings.fileAssociationHint") }} + +
+ +
-
- {{ associationMessage }} + +
+ + +
+ +
+ + + {{ pdfStyle.fontSize }}px +
+ +
+ + + {{ pdfStyle.lineHeight.toFixed(2) }} +
+ +
+ +
+ + + +
+
+ +
+ + + {{ pdfStyle.textColor }} +
+ +
+ + + {{ pdfStyle.headingColor }}
-
- -
-
-
-
{{ t("shortcuts.title") }}
-
{{ t("shortcuts.hint") }}
+
+ + + {{ pdfStyle.linkColor }} +
+ +
+ + + {{ pdfStyle.codeBg }} +
+ +
+ + + {{ pdfStyle.bgColor }} +
+ +
+ + + +
+ +
+ + + {{ pdfStyle.pageMargin }}mm +
+ +
+ +
- -
-
+
- @@ -90,7 +101,9 @@ export async function exportToHtml( filters: [{ name: "HTML", extensions: ["html", "htm"] }], }); if (!dest) return null; - const html = await buildExportHtml(body, baseName); + const html = await buildExportHtml(body, baseName, { + pdfStyle: usePdfStyle().settings.value, + }); await writeTextFile(dest, html); return dest; } @@ -132,7 +145,10 @@ async function pandocExport( filters: [{ name: prettyName, extensions: [ext] }], }); if (!dest) return null; - const html = await buildExportHtml(body, title, { forceLight: true }); + const html = await buildExportHtml(body, title, { + forceLight: true, + pdfStyle: getDefaultPdfStyle(), + }); return await invoke("export_with_pandoc", { opts: { html, @@ -212,7 +228,9 @@ export async function exportToPdf( filters: [{ name: "PDF", extensions: ["pdf"] }], }); if (!dest) return null; - const html = await buildExportHtml(body, title, { forceLight: true }); + const html = await buildExportHtml(body, title, { + pdfStyle: usePdfStyle().settings.value, + }); const cached = getCachedEdgePath(); try { return await callEdge(html, dest, cached); @@ -229,7 +247,7 @@ export async function exportToPdf( } export function printDocument(body: HTMLElement, title: string) { - buildExportHtml(body, title, { forceLight: true }) + buildExportHtml(body, title, { pdfStyle: usePdfStyle().settings.value }) .then((html) => { const blob = new globalThis.Blob([html], { type: "text/html" }); const url = globalThis.URL.createObjectURL(blob); diff --git a/src/composables/usePdfPreview.ts b/src/composables/usePdfPreview.ts new file mode 100644 index 0000000..eea9214 --- /dev/null +++ b/src/composables/usePdfPreview.ts @@ -0,0 +1,130 @@ +// Live preview for the PDF export style panel. Reuses the exact same pipeline as +// the real export (renderMarkdown + buildExportHtml) so what you see is what you get, +// rendered in isolated