fix: 完善本地与网络图片渲染

This commit is contained in:
SkyJourney
2026-07-27 22:12:16 +08:00
parent 06b30d084e
commit 32a37293d0
31 changed files with 1204 additions and 71 deletions
+8 -4
View File
@@ -1,8 +1,9 @@
# Desktop 桌面端
`apps/desktop``v0.4.0` 新增的 Electron 桌面应用。当前已经完成
Electron 壳、受限 preload、进程内应用服务、隐藏分页窗口、Electron
Chromium PDF 输出、原生 PDF 另存为和 Windows 安装包验证。
Electron 壳、受限 preload、进程内应用服务、原生 Markdown 文件选择、
同目录图片解析、隐藏分页窗口、Electron Chromium PDF 输出、原生 PDF
另存为和 Windows 安装包验证。
## 开发
@@ -26,7 +27,7 @@ npm run make -w @md-to-pdf/desktop
```
版本化目录包、Squirrel `Setup.exe` 和 ZIP 输出到
`apps/desktop/out/v0.4.0/`。该目录被 Git 忽略。公司内部分发以
`apps/desktop/out/v0.4.1/`。该目录被 Git 忽略。公司内部分发以
`Setup.exe` 为正式安装包,ZIP 作为免安装辅助包;当前未配置代码签名,
Windows 首次运行可能显示“未知发布者”提示。
@@ -42,7 +43,10 @@ Web 静态资源作为只读资源放在 Electron `resources/dist` 下,生产
- 应用窗口和 PDF 窗口均关闭 Node Integration
- 启用 Context Isolation 和 Chromium Sandbox
- preload 只暴露渲染、主题、生成 PDF 和保存 PDF 所需的窄接口;
- preload 只暴露打开 Markdown、渲染、主题、生成 PDF 和保存 PDF 所需
的窄接口;
- 本地图片只能从当前 Markdown 所在目录内读取,并拒绝路径穿越和符号
链接越界;
- 主进程校验 IPC 发送者和分页载荷;
- PDF 窗口使用独立内存 Session,并阻止外部网络资源;
- 应用页面使用自定义协议,不使用 `file://`
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@md-to-pdf/desktop",
"version": "0.4.0",
"version": "0.4.1",
"private": true,
"productName": "Markdown PDF 导出器",
"description": "Markdown PDF 导出器桌面端",
@@ -8,7 +8,7 @@
"type": "module",
"main": "dist/main.js",
"dependencies": {
"@md-to-pdf/application": "0.4.0"
"@md-to-pdf/application": "0.4.1"
},
"scripts": {
"build": "npm run clean && npm run build:main && npm run build:preload",
+2
View File
@@ -5,11 +5,13 @@ import {
DESKTOP_GET_THEME_CSS,
DESKTOP_GENERATE_PDF,
DESKTOP_LIST_THEMES,
DESKTOP_OPEN_MARKDOWN,
DESKTOP_RENDER_MARKDOWN,
DESKTOP_SAVE_PDF
} from "./channels.js";
contextBridge.exposeInMainWorld("mdToPdfDesktop", {
openMarkdown: () => ipcRenderer.invoke(DESKTOP_OPEN_MARKDOWN),
renderMarkdown: (input: MarkdownRenderRequest) =>
ipcRenderer.invoke(DESKTOP_RENDER_MARKDOWN, input),
listThemes: () => ipcRenderer.invoke(DESKTOP_LIST_THEMES),
+2 -1
View File
@@ -12,7 +12,8 @@ export function parseMarkdownRenderRequest(
}
return {
markdown: value.markdown,
language: value.language
language: value.language,
resources: value.resources
};
}
+2
View File
@@ -1,5 +1,7 @@
export const DESKTOP_RENDER_MARKDOWN =
"md-to-pdf:desktop:render-markdown";
export const DESKTOP_OPEN_MARKDOWN =
"md-to-pdf:desktop:open-markdown";
export const DESKTOP_LIST_THEMES =
"md-to-pdf:desktop:list-themes";
export const DESKTOP_GET_THEME_CSS =
+29 -2
View File
@@ -8,7 +8,7 @@ import {
session,
type IpcMainInvokeEvent
} from "electron";
import { access, writeFile } from "node:fs/promises";
import { access, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import {
@@ -19,6 +19,7 @@ import {
DESKTOP_GET_THEME_CSS,
DESKTOP_GENERATE_PDF,
DESKTOP_LIST_THEMES,
DESKTOP_OPEN_MARKDOWN,
DESKTOP_RENDER_MARKDOWN,
DESKTOP_SAVE_PDF
} from "./channels.js";
@@ -229,7 +230,31 @@ async function createApplication(
event.senderFrame !== null &&
new URL(event.senderFrame.url).origin ===
new URL(applicationUrl).origin;
let documentRoot: string | undefined;
ipcMain.handle(DESKTOP_OPEN_MARKDOWN, async (event) => {
if (!validateSender(event)) {
throw new Error("拒绝未授权的桌面文件请求");
}
const selection = await dialog.showOpenDialog(window, {
title: "打开 Markdown",
properties: ["openFile"],
filters: [
{ name: "Markdown 文件", extensions: ["md", "markdown"] },
{ name: "文本文件", extensions: ["txt"] }
]
});
const filePath = selection.filePaths[0];
if (selection.canceled || !filePath) {
return undefined;
}
const content = await readFile(filePath, "utf8");
documentRoot = path.dirname(filePath);
return {
markdown: content,
fileName: path.basename(filePath)
};
});
ipcMain.handle(
DESKTOP_RENDER_MARKDOWN,
async (event, unsafeRequest: unknown) => {
@@ -237,7 +262,8 @@ async function createApplication(
throw new Error("拒绝未授权的桌面渲染请求");
}
return applicationService.render(
parseMarkdownRenderRequest(unsafeRequest)
parseMarkdownRenderRequest(unsafeRequest),
documentRoot ? { localRoot: documentRoot } : {}
);
}
);
@@ -317,6 +343,7 @@ async function createApplication(
window.once("ready-to-show", () => window.show());
window.on("closed", () => {
ipcMain.removeHandler(DESKTOP_OPEN_MARKDOWN);
ipcMain.removeHandler(DESKTOP_RENDER_MARKDOWN);
ipcMain.removeHandler(DESKTOP_LIST_THEMES);
ipcMain.removeHandler(DESKTOP_GET_THEME_CSS);