feat: 完成 v0.6.0 墨呈品牌迁移
This commit is contained in:
@@ -5,7 +5,7 @@ YIXIONG Tech.ltd 公司内部专属软件许可证
|
|||||||
|
|
||||||
一、适用范围
|
一、适用范围
|
||||||
|
|
||||||
本许可证适用于“Markdown PDF 导出器”项目中由 YIXIONG Tech.ltd 拥有
|
本许可证适用于“墨呈(MorphDoc)”项目中由 YIXIONG Tech.ltd 拥有
|
||||||
著作权的源代码、原创主题、原创文档、构建脚本和其他原创材料(以下简称
|
著作权的源代码、原创主题、原创文档、构建脚本和其他原创材料(以下简称
|
||||||
“本软件”)。
|
“本软件”)。
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Markdown PDF 导出器
|
墨呈(MorphDoc)
|
||||||
Copyright 2026 YIXIONG Tech.ltd
|
Copyright 2026 YIXIONG Tech.ltd
|
||||||
|
|
||||||
This product includes software developed by YIXIONG Tech.ltd and
|
This product includes software developed by YIXIONG Tech.ltd and
|
||||||
|
|||||||
@@ -1,12 +1,25 @@
|
|||||||
# Markdown PDF 导出器
|
# 墨呈
|
||||||
|
|
||||||
一个同时提供 Web 与 Windows 桌面端的 Markdown 排版、分页预览和 PDF
|
> 一稿多式,即写即呈
|
||||||
导出工具。预览与 PDF 复用同一份安全 HTML、`#write` DOM、主题 CSS 和
|
|
||||||
分页运行时,适合内网部署,也可以作为不依赖本地服务的桌面应用使用。
|
|
||||||
|
|
||||||
当前版本:`v0.5.1`。
|
面向结构化 Markdown 的主题化文档创作与发布工具,同时提供 Web 与
|
||||||
|
Windows 桌面端。预览与 PDF 复用同一份安全 HTML、`#write` DOM、主题
|
||||||
|
CSS 和分页运行时,DOCX 使用同源语义文档、主题令牌和可编辑 OOXML。
|
||||||
|
|
||||||
## v0.5.1 主要能力
|
当前版本:`v0.6.0`。
|
||||||
|
|
||||||
|
## v0.6.0 核心更新
|
||||||
|
|
||||||
|
- 新增可编辑 DOCX 导出,使用固定 Pandoc、动态 Word 样式、原生分节、
|
||||||
|
页眉页脚、页码和高分辨率 PNG 媒体管线;
|
||||||
|
- 主题 CSS 通过通用语义槽位翻译为 Word 样式,支持 14 套内置主题、
|
||||||
|
公文结构、独立封面、代码、表格、图片、Mermaid 与 ECharts;
|
||||||
|
- 新增 CodeMirror 6 Markdown 工具栏,支持标题、行内格式、引用、列表、
|
||||||
|
代码块、分隔线和尺寸表格;
|
||||||
|
- 产品品牌升级为“墨呈”,Windows 主程序和发行产物使用英文名
|
||||||
|
`MorphDoc`。
|
||||||
|
|
||||||
|
## v0.5.1 主题与公文能力
|
||||||
|
|
||||||
- 新增政企标准红头、函件红头、企业简版红头、简报红头等 4 套红头主题,
|
- 新增政企标准红头、函件红头、企业简版红头、简报红头等 4 套红头主题,
|
||||||
以及 3 套正式文档主题和 3 套标书主题;
|
以及 3 套正式文档主题和 3 套标书主题;
|
||||||
@@ -39,6 +52,7 @@
|
|||||||
| 远程图片 | 服务端受限下载并内嵌 | 应用服务受限下载并内嵌 |
|
| 远程图片 | 服务端受限下载并内嵌 | 应用服务受限下载并内嵌 |
|
||||||
| 自定义主题 | Compose 只读挂载目录 | “更多 → 打开自定义主题目录” |
|
| 自定义主题 | Compose 只读挂载目录 | “更多 → 打开自定义主题目录” |
|
||||||
| PDF 引擎 | 固定版本 Playwright Chromium | Electron 自带 Chromium |
|
| PDF 引擎 | 固定版本 Playwright Chromium | Electron 自带 Chromium |
|
||||||
|
| DOCX 引擎 | 固定版本 Pandoc | 内置固定版本 Pandoc |
|
||||||
|
|
||||||
Web 端不提供本地素材目录能力。需要包含本地相对资源的文档时,请使用
|
Web 端不提供本地素材目录能力。需要包含本地相对资源的文档时,请使用
|
||||||
Desktop;Web 端可使用 Base64/Data URL 或受限公网图片。
|
Desktop;Web 端可使用 Base64/Data URL 或受限公网图片。
|
||||||
@@ -126,8 +140,8 @@ npm run make -w @md-to-pdf/desktop
|
|||||||
```
|
```
|
||||||
|
|
||||||
桌面 `package` 和 `make` 会先完整重建内嵌 Web 及全部共享依赖,不能
|
桌面 `package` 和 `make` 会先完整重建内嵌 Web 及全部共享依赖,不能
|
||||||
复用陈旧的 `apps/web/dist`。产物位于 `apps/desktop/out/v0.5.1/`。
|
复用陈旧的 `apps/web/dist`。产物位于 `apps/desktop/out/v0.6.0/`。
|
||||||
正式安装包命名为 `md-to-pdf-0.5.1-x86_64-Setup.exe`,同时生成免安装 ZIP;当前发行包
|
正式安装包命名为 `MorphDoc-0.6.0-x86_64-Setup.exe`,同时生成免安装 ZIP;当前发行包
|
||||||
未配置代码签名。
|
未配置代码签名。
|
||||||
|
|
||||||
## 文档与媒体
|
## 文档与媒体
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ Markdown ECharts、Core、Renderer、Application、Preview Engine 和 Web,
|
|||||||
然后才复制 `apps/web/dist`。不得绕过该链路直接调用 electron-builder。
|
然后才复制 `apps/web/dist`。不得绕过该链路直接调用 electron-builder。
|
||||||
|
|
||||||
版本化目录包、NSIS `Setup.exe` 和 ZIP 输出到
|
版本化目录包、NSIS `Setup.exe` 和 ZIP 输出到
|
||||||
`apps/desktop/out/v0.5.1/`。该目录被 Git 忽略。公司内部分发以
|
`apps/desktop/out/v0.6.0/`。该目录被 Git 忽略。公司内部分发以
|
||||||
`md-to-pdf-0.5.1-x86_64-Setup.exe` 为正式安装包,ZIP 作为免安装
|
`MorphDoc-0.6.0-x86_64-Setup.exe` 为正式安装包,ZIP 作为免安装
|
||||||
辅助包;当前未配置代码签名,Windows 首次运行可能显示“未知发布者”
|
辅助包;当前未配置代码签名,Windows 首次运行可能显示“未知发布者”
|
||||||
提示。
|
提示。
|
||||||
|
|
||||||
@@ -56,11 +56,12 @@ NSIS 使用标准辅助安装模式,支持选择当前用户/所有用户安
|
|||||||
发布,不提交进 Git 历史。
|
发布,不提交进 Git 历史。
|
||||||
|
|
||||||
桌面和开始菜单快捷方式由 `build/installer.nsh` 显式创建,安装完成后的
|
桌面和开始菜单快捷方式由 `build/installer.nsh` 显式创建,安装完成后的
|
||||||
“运行”操作直接启动 `md-to-pdf.exe`,不依赖快捷方式文件。
|
“运行”操作直接启动 `MorphDoc.exe`,不依赖快捷方式文件。
|
||||||
|
|
||||||
产品名称、窗口标题、安装包名称和开始菜单名称保持为
|
界面、快捷方式与卸载项使用中文名“墨呈”,窗口标题为
|
||||||
“Markdown PDF 导出器”,实际 Windows 主程序与进程文件固定为
|
“墨呈 · MorphDoc”,默认安装目录和 Windows 主程序分别为 `MorphDoc`
|
||||||
`md-to-pdf.exe`。
|
和 `MorphDoc.exe`。NSIS appId 保持 `com.md-to-pdf.desktop`,确保
|
||||||
|
v0.5.1 覆盖升级沿用原安装身份。
|
||||||
|
|
||||||
当前目录包只包含一套 Electron Chromium,不携带 Playwright Chromium。
|
当前目录包只包含一套 Electron Chromium,不携带 Playwright Chromium。
|
||||||
Web 静态资源作为只读资源放在 Electron `resources/dist` 下,生产环境通过
|
Web 静态资源作为只读资源放在 Electron `resources/dist` 下,生产环境通过
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
!define MORPHDOC_DISPLAY_NAME "墨呈"
|
||||||
|
!define MORPHDOC_EXECUTABLE "MorphDoc.exe"
|
||||||
|
!define MORPHDOC_LEGACY_DISPLAY_NAME "Markdown PDF 导出器"
|
||||||
|
!define MORPHDOC_LEGACY_EXECUTABLE "md-to-pdf.exe"
|
||||||
|
|
||||||
!ifdef FONT_PACK_COMPANION_FILE
|
!ifdef FONT_PACK_COMPANION_FILE
|
||||||
!ifndef BUILD_UNINSTALLER
|
!ifndef BUILD_UNINSTALLER
|
||||||
!include "FileFunc.nsh"
|
!include "FileFunc.nsh"
|
||||||
@@ -36,7 +41,7 @@
|
|||||||
Pop $R0
|
Pop $R0
|
||||||
StrCmp $R0 "error" 0 +2
|
StrCmp $R0 "error" 0 +2
|
||||||
Abort
|
Abort
|
||||||
${NSD_CreateLabel} 0 0 100% 28u "检测到经过完整性校验的可选字体包。安装后,政企公文主题可使用与 PDF 相同的原生中文字体资产。"
|
${NSD_CreateLabel} 0 0 100% 28u "检测到经过完整性校验的墨呈可选字体包。安装后,政企公文主题可使用与 PDF 相同的原生中文字体资产。"
|
||||||
Pop $R0
|
Pop $R0
|
||||||
${NSD_CreateCheckbox} 0 38u 100% 14u "安装 ${FONT_PACK_COMPANION_NAME}"
|
${NSD_CreateCheckbox} 0 38u 100% 14u "安装 ${FONT_PACK_COMPANION_NAME}"
|
||||||
Pop $fontPackCompanionCheckbox
|
Pop $fontPackCompanionCheckbox
|
||||||
@@ -70,11 +75,11 @@
|
|||||||
WriteRegStr SHCTX \
|
WriteRegStr SHCTX \
|
||||||
"Software\Classes\MarkdownPdfExporter.Markdown\DefaultIcon" \
|
"Software\Classes\MarkdownPdfExporter.Markdown\DefaultIcon" \
|
||||||
"" \
|
"" \
|
||||||
'"$INSTDIR\md-to-pdf.exe",0'
|
'"$INSTDIR\${MORPHDOC_EXECUTABLE}",0'
|
||||||
WriteRegStr SHCTX \
|
WriteRegStr SHCTX \
|
||||||
"Software\Classes\MarkdownPdfExporter.Markdown\shell\open\command" \
|
"Software\Classes\MarkdownPdfExporter.Markdown\shell\open\command" \
|
||||||
"" \
|
"" \
|
||||||
'"$INSTDIR\md-to-pdf.exe" "%1"'
|
'"$INSTDIR\${MORPHDOC_EXECUTABLE}" "%1"'
|
||||||
WriteRegStr SHCTX \
|
WriteRegStr SHCTX \
|
||||||
"Software\Classes\.md\OpenWithProgids" \
|
"Software\Classes\.md\OpenWithProgids" \
|
||||||
"MarkdownPdfExporter.Markdown" \
|
"MarkdownPdfExporter.Markdown" \
|
||||||
@@ -84,29 +89,34 @@
|
|||||||
"MarkdownPdfExporter.Markdown" \
|
"MarkdownPdfExporter.Markdown" \
|
||||||
""
|
""
|
||||||
WriteRegStr SHCTX \
|
WriteRegStr SHCTX \
|
||||||
"Software\Classes\Applications\md-to-pdf.exe\shell\open\command" \
|
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}\shell\open\command" \
|
||||||
"" \
|
"" \
|
||||||
'"$INSTDIR\md-to-pdf.exe" "%1"'
|
'"$INSTDIR\${MORPHDOC_EXECUTABLE}" "%1"'
|
||||||
WriteRegStr SHCTX \
|
WriteRegStr SHCTX \
|
||||||
"Software\Classes\Applications\md-to-pdf.exe\SupportedTypes" \
|
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}\SupportedTypes" \
|
||||||
".md" \
|
".md" \
|
||||||
""
|
""
|
||||||
WriteRegStr SHCTX \
|
WriteRegStr SHCTX \
|
||||||
"Software\Classes\Applications\md-to-pdf.exe\SupportedTypes" \
|
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}\SupportedTypes" \
|
||||||
".markdown" \
|
".markdown" \
|
||||||
""
|
""
|
||||||
CreateDirectory "$SMPROGRAMS\Markdown PDF 导出器"
|
DeleteRegKey SHCTX \
|
||||||
|
"Software\Classes\Applications\${MORPHDOC_LEGACY_EXECUTABLE}"
|
||||||
|
Delete "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
|
||||||
|
RMDir "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}"
|
||||||
|
Delete "$DESKTOP\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
|
||||||
|
CreateDirectory "$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}"
|
||||||
CreateShortCut \
|
CreateShortCut \
|
||||||
"$SMPROGRAMS\Markdown PDF 导出器\Markdown PDF 导出器.lnk" \
|
"$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}\${MORPHDOC_DISPLAY_NAME}.lnk" \
|
||||||
"$INSTDIR\md-to-pdf.exe" \
|
"$INSTDIR\${MORPHDOC_EXECUTABLE}" \
|
||||||
"" \
|
"" \
|
||||||
"$INSTDIR\md-to-pdf.exe"
|
"$INSTDIR\${MORPHDOC_EXECUTABLE}"
|
||||||
CreateShortCut \
|
CreateShortCut \
|
||||||
"$DESKTOP\Markdown PDF 导出器.lnk" \
|
"$DESKTOP\${MORPHDOC_DISPLAY_NAME}.lnk" \
|
||||||
"$INSTDIR\md-to-pdf.exe" \
|
"$INSTDIR\${MORPHDOC_EXECUTABLE}" \
|
||||||
"" \
|
"" \
|
||||||
"$INSTDIR\md-to-pdf.exe"
|
"$INSTDIR\${MORPHDOC_EXECUTABLE}"
|
||||||
StrCpy $launchLink "$INSTDIR\md-to-pdf.exe"
|
StrCpy $launchLink "$INSTDIR\${MORPHDOC_EXECUTABLE}"
|
||||||
!ifdef FONT_PACK_COMPANION_FILE
|
!ifdef FONT_PACK_COMPANION_FILE
|
||||||
StrCmp $fontPackCompanionAvailable "1" 0 font_pack_install_done
|
StrCmp $fontPackCompanionAvailable "1" 0 font_pack_install_done
|
||||||
StrCmp $fontPackCompanionSelected "1" 0 font_pack_install_done
|
StrCmp $fontPackCompanionSelected "1" 0 font_pack_install_done
|
||||||
@@ -136,11 +146,16 @@
|
|||||||
"Software\Classes\.markdown\OpenWithProgids" \
|
"Software\Classes\.markdown\OpenWithProgids" \
|
||||||
"MarkdownPdfExporter.Markdown"
|
"MarkdownPdfExporter.Markdown"
|
||||||
DeleteRegKey SHCTX \
|
DeleteRegKey SHCTX \
|
||||||
"Software\Classes\Applications\md-to-pdf.exe"
|
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}"
|
||||||
|
DeleteRegKey SHCTX \
|
||||||
|
"Software\Classes\Applications\${MORPHDOC_LEGACY_EXECUTABLE}"
|
||||||
DeleteRegKey SHCTX \
|
DeleteRegKey SHCTX \
|
||||||
"Software\Classes\MarkdownPdfExporter.Markdown"
|
"Software\Classes\MarkdownPdfExporter.Markdown"
|
||||||
Delete "$SMPROGRAMS\Markdown PDF 导出器\Markdown PDF 导出器.lnk"
|
Delete "$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}\${MORPHDOC_DISPLAY_NAME}.lnk"
|
||||||
RMDir "$SMPROGRAMS\Markdown PDF 导出器"
|
RMDir "$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}"
|
||||||
Delete "$DESKTOP\Markdown PDF 导出器.lnk"
|
Delete "$DESKTOP\${MORPHDOC_DISPLAY_NAME}.lnk"
|
||||||
|
Delete "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
|
||||||
|
RMDir "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}"
|
||||||
|
Delete "$DESKTOP\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
|
||||||
System::Call 'shell32::SHChangeNotify(i 0x08000000, i 0, i 0, i 0)'
|
System::Call 'shell32::SHChangeNotify(i 0x08000000, i 0, i 0, i 0)'
|
||||||
!macroend
|
!macroend
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const path = require("node:path");
|
const path = require("node:path");
|
||||||
const { version } = require("./package.json");
|
const { version } = require("./package.json");
|
||||||
|
const brand = require("../../product.json");
|
||||||
|
|
||||||
const windowsIcon = path.resolve(
|
const windowsIcon = path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
@@ -9,8 +10,8 @@ const nsisInclude = process.env.MD_TO_PDF_NSIS_INCLUDE?.trim() ||
|
|||||||
"build/installer.nsh";
|
"build/installer.nsh";
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
appId: "com.md-to-pdf.desktop",
|
appId: brand.appId,
|
||||||
productName: "Markdown PDF 导出器",
|
productName: brand.englishName,
|
||||||
copyright: "Copyright © YIXIONG Tech.ltd",
|
copyright: "Copyright © YIXIONG Tech.ltd",
|
||||||
asar: true,
|
asar: true,
|
||||||
electronLanguages: ["zh-CN", "en-US"],
|
electronLanguages: ["zh-CN", "en-US"],
|
||||||
@@ -58,8 +59,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
icon: windowsIcon,
|
icon: windowsIcon,
|
||||||
executableName: "md-to-pdf",
|
executableName: brand.executableName,
|
||||||
artifactName: `md-to-pdf-${version}-x86_64.\${ext}`
|
artifactName: `${brand.artifactPrefix}-${version}-x86_64.\${ext}`
|
||||||
},
|
},
|
||||||
nsis: {
|
nsis: {
|
||||||
oneClick: false,
|
oneClick: false,
|
||||||
@@ -68,12 +69,12 @@ module.exports = {
|
|||||||
allowElevation: true,
|
allowElevation: true,
|
||||||
createDesktopShortcut: false,
|
createDesktopShortcut: false,
|
||||||
createStartMenuShortcut: false,
|
createStartMenuShortcut: false,
|
||||||
shortcutName: "Markdown PDF 导出器",
|
shortcutName: brand.displayName,
|
||||||
uninstallDisplayName: "Markdown PDF 导出器",
|
uninstallDisplayName: brand.displayName,
|
||||||
installerIcon: windowsIcon,
|
installerIcon: windowsIcon,
|
||||||
uninstallerIcon: windowsIcon,
|
uninstallerIcon: windowsIcon,
|
||||||
installerLanguages: ["zh_CN"],
|
installerLanguages: ["zh_CN"],
|
||||||
include: nsisInclude,
|
include: nsisInclude,
|
||||||
artifactName: `md-to-pdf-${version}-x86_64-Setup.\${ext}`
|
artifactName: `${brand.artifactPrefix}-${version}-x86_64-Setup.\${ext}`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "@md-to-pdf/desktop",
|
"name": "@md-to-pdf/desktop",
|
||||||
"version": "0.5.1",
|
"version": "0.6.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"productName": "Markdown PDF 导出器",
|
"productName": "MorphDoc",
|
||||||
"description": "Markdown PDF 导出器桌面端",
|
"description": "墨呈桌面端:面向结构化 Markdown 的主题化文档创作与发布工具",
|
||||||
"author": "YIXIONG Tech.ltd",
|
"author": "YIXIONG Tech.ltd",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import { build } from "esbuild";
|
import { build } from "esbuild";
|
||||||
import { copyFile } from "node:fs/promises";
|
import { copyFile, readFile } from "node:fs/promises";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const productBrand = JSON.parse(
|
||||||
|
await readFile(
|
||||||
|
fileURLToPath(new URL("../../../product.json", import.meta.url)),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
await build({
|
await build({
|
||||||
entryPoints: ["src/main.ts"],
|
entryPoints: ["src/main.ts"],
|
||||||
bundle: true,
|
bundle: true,
|
||||||
@@ -9,6 +16,15 @@ await build({
|
|||||||
format: "esm",
|
format: "esm",
|
||||||
external: ["electron"],
|
external: ["electron"],
|
||||||
outfile: "dist/main.js",
|
outfile: "dist/main.js",
|
||||||
|
define: {
|
||||||
|
__APP_ID__: JSON.stringify(productBrand.appId),
|
||||||
|
__APP_ENGLISH_NAME__: JSON.stringify(productBrand.englishName),
|
||||||
|
__APP_DISPLAY_NAME__: JSON.stringify(productBrand.displayName),
|
||||||
|
__APP_WINDOW_TITLE__: JSON.stringify(productBrand.windowTitle),
|
||||||
|
__APP_LEGACY_DISPLAY_NAME__: JSON.stringify(
|
||||||
|
productBrand.legacyDisplayName
|
||||||
|
)
|
||||||
|
},
|
||||||
banner: {
|
banner: {
|
||||||
js: [
|
js: [
|
||||||
'import { createRequire as __mdToPdfCreateRequire } from "node:module";',
|
'import { createRequire as __mdToPdfCreateRequire } from "node:module";',
|
||||||
|
|||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
declare const __APP_ID__: string;
|
||||||
|
declare const __APP_ENGLISH_NAME__: string;
|
||||||
|
declare const __APP_DISPLAY_NAME__: string;
|
||||||
|
declare const __APP_WINDOW_TITLE__: string;
|
||||||
|
declare const __APP_LEGACY_DISPLAY_NAME__: string;
|
||||||
@@ -233,7 +233,7 @@ export class DesktopApplicationController {
|
|||||||
: undefined;
|
: undefined;
|
||||||
const initialBounds = this.#resolveInitialBounds(isPrimary);
|
const initialBounds = this.#resolveInitialBounds(isPrimary);
|
||||||
const window = new BrowserWindow({
|
const window = new BrowserWindow({
|
||||||
title: `Markdown PDF 导出器 v${app.getVersion()}`,
|
title: `${__APP_WINDOW_TITLE__} v${app.getVersion()}`,
|
||||||
icon: this.#iconPath,
|
icon: this.#iconPath,
|
||||||
...initialBounds,
|
...initialBounds,
|
||||||
minWidth: Math.min(
|
minWidth: Math.min(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
session
|
session
|
||||||
} from "electron";
|
} from "electron";
|
||||||
import { access, mkdir } from "node:fs/promises";
|
import { access, mkdir } from "node:fs/promises";
|
||||||
|
import { existsSync } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||||
import {
|
import {
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
findMarkdownFileArgument,
|
findMarkdownFileArgument,
|
||||||
isMarkdownFilePath
|
isMarkdownFilePath
|
||||||
} from "./markdown-file.js";
|
} from "./markdown-file.js";
|
||||||
|
import { migrateLegacyUserData } from "./user-data-migration.js";
|
||||||
|
|
||||||
const APP_SCHEME = "mdpdf";
|
const APP_SCHEME = "mdpdf";
|
||||||
const APP_HOST = "bundle";
|
const APP_HOST = "bundle";
|
||||||
@@ -37,6 +39,28 @@ let pendingExternalMarkdownPath = findMarkdownFileArgument(
|
|||||||
process.cwd()
|
process.cwd()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function configurePackagedUserDataDirectory() {
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const appDataDirectory = app.getPath("appData");
|
||||||
|
const targetDirectory = path.join(
|
||||||
|
appDataDirectory,
|
||||||
|
__APP_ENGLISH_NAME__
|
||||||
|
);
|
||||||
|
const targetAlreadyExists = existsSync(targetDirectory);
|
||||||
|
app.setPath("userData", targetDirectory);
|
||||||
|
if (targetAlreadyExists) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return migrateLegacyUserData(
|
||||||
|
[path.join(appDataDirectory, __APP_LEGACY_DISPLAY_NAME__)],
|
||||||
|
targetDirectory
|
||||||
|
).then(() => undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const userDataMigration = configurePackagedUserDataDirectory();
|
||||||
|
|
||||||
protocol.registerSchemesAsPrivileged([
|
protocol.registerSchemesAsPrivileged([
|
||||||
{
|
{
|
||||||
scheme: APP_SCHEME,
|
scheme: APP_SCHEME,
|
||||||
@@ -300,9 +324,10 @@ if (!hasSingleInstanceLock) {
|
|||||||
app
|
app
|
||||||
.whenReady()
|
.whenReady()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
|
await userDataMigration;
|
||||||
Menu.setApplicationMenu(null);
|
Menu.setApplicationMenu(null);
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
app.setAppUserModelId("com.md-to-pdf.desktop");
|
app.setAppUserModelId(__APP_ID__);
|
||||||
}
|
}
|
||||||
const themeDirectory = getDesktopThemeDirectory();
|
const themeDirectory = getDesktopThemeDirectory();
|
||||||
await mkdir(themeDirectory, { recursive: true });
|
await mkdir(themeDirectory, { recursive: true });
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { cp, lstat, mkdir } from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
const MIGRATED_ENTRIES = [
|
||||||
|
"window-state.json",
|
||||||
|
"save-location.json",
|
||||||
|
"themes",
|
||||||
|
"Local Storage",
|
||||||
|
"Session Storage",
|
||||||
|
"IndexedDB"
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
async function readOrdinaryStatus(target: string) {
|
||||||
|
try {
|
||||||
|
const status = await lstat(target);
|
||||||
|
return status.isSymbolicLink() ? undefined : status;
|
||||||
|
} catch (error) {
|
||||||
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LegacyUserDataMigrationResult {
|
||||||
|
migrated: boolean;
|
||||||
|
sourceDirectory?: string;
|
||||||
|
copiedEntries: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function migrateLegacyUserData(
|
||||||
|
legacyDirectories: readonly string[],
|
||||||
|
targetDirectory: string
|
||||||
|
): Promise<LegacyUserDataMigrationResult> {
|
||||||
|
if (await readOrdinaryStatus(targetDirectory)) {
|
||||||
|
return { migrated: false, copiedEntries: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const legacyDirectory of legacyDirectories) {
|
||||||
|
if (path.resolve(legacyDirectory) === path.resolve(targetDirectory)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const legacyStatus = await readOrdinaryStatus(legacyDirectory);
|
||||||
|
if (!legacyStatus?.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mkdir(targetDirectory, { recursive: true });
|
||||||
|
const copiedEntries: string[] = [];
|
||||||
|
for (const entry of MIGRATED_ENTRIES) {
|
||||||
|
const source = path.join(legacyDirectory, entry);
|
||||||
|
if (!(await readOrdinaryStatus(source))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await cp(source, path.join(targetDirectory, entry), {
|
||||||
|
recursive: true,
|
||||||
|
errorOnExist: true,
|
||||||
|
force: false
|
||||||
|
});
|
||||||
|
copiedEntries.push(entry);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
migrated: true,
|
||||||
|
sourceDirectory: legacyDirectory,
|
||||||
|
copiedEntries
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { migrated: false, copiedEntries: [] };
|
||||||
|
}
|
||||||
@@ -3,14 +3,25 @@ import { fileURLToPath } from "node:url";
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
interface PackageManifest {
|
interface PackageManifest {
|
||||||
|
version?: string;
|
||||||
|
productName?: string;
|
||||||
scripts?: Record<string, string>;
|
scripts?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ProductBrand {
|
||||||
|
appId: string;
|
||||||
|
englishName: string;
|
||||||
|
displayName: string;
|
||||||
|
windowTitle: string;
|
||||||
|
executableName: string;
|
||||||
|
artifactPrefix: string;
|
||||||
|
}
|
||||||
|
|
||||||
const desktopRoot = fileURLToPath(new URL("../", import.meta.url));
|
const desktopRoot = fileURLToPath(new URL("../", import.meta.url));
|
||||||
const projectRoot = fileURLToPath(new URL("../../../", import.meta.url));
|
const projectRoot = fileURLToPath(new URL("../../../", import.meta.url));
|
||||||
|
|
||||||
function readManifest(path: string): PackageManifest {
|
function readManifest<T = PackageManifest>(path: string): T {
|
||||||
return JSON.parse(readFileSync(path, "utf8")) as PackageManifest;
|
return JSON.parse(readFileSync(path, "utf8")) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("桌面发行构建链", () => {
|
describe("桌面发行构建链", () => {
|
||||||
@@ -58,6 +69,49 @@ describe("桌面发行构建链", () => {
|
|||||||
expect(builderConfig).toContain('to: "samples"');
|
expect(builderConfig).toContain('to: "samples"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("保持安装身份并从统一配置生成 MorphDoc 发行名称", () => {
|
||||||
|
const brand = readManifest<ProductBrand>(
|
||||||
|
`${projectRoot}/product.json`
|
||||||
|
);
|
||||||
|
const desktopManifest = readManifest(
|
||||||
|
`${desktopRoot}/package.json`
|
||||||
|
);
|
||||||
|
const builderConfig = readFileSync(
|
||||||
|
`${desktopRoot}/electron-builder.config.cjs`,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
const installerInclude = readFileSync(
|
||||||
|
`${desktopRoot}/build/installer.nsh`,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(brand).toMatchObject({
|
||||||
|
appId: "com.md-to-pdf.desktop",
|
||||||
|
englishName: "MorphDoc",
|
||||||
|
displayName: "墨呈",
|
||||||
|
windowTitle: "墨呈 · MorphDoc",
|
||||||
|
executableName: "MorphDoc",
|
||||||
|
artifactPrefix: "MorphDoc"
|
||||||
|
});
|
||||||
|
expect(desktopManifest).toMatchObject({
|
||||||
|
version: "0.6.0",
|
||||||
|
productName: "MorphDoc"
|
||||||
|
});
|
||||||
|
expect(builderConfig).toContain("appId: brand.appId");
|
||||||
|
expect(builderConfig).toContain("productName: brand.englishName");
|
||||||
|
expect(builderConfig).toContain("executableName: brand.executableName");
|
||||||
|
expect(builderConfig).toContain("shortcutName: brand.displayName");
|
||||||
|
expect(installerInclude).toContain(
|
||||||
|
'!define MORPHDOC_EXECUTABLE "MorphDoc.exe"'
|
||||||
|
);
|
||||||
|
expect(installerInclude).toContain(
|
||||||
|
'!define MORPHDOC_DISPLAY_NAME "墨呈"'
|
||||||
|
);
|
||||||
|
expect(installerInclude).toContain(
|
||||||
|
"Software\\Classes\\Applications\\${MORPHDOC_LEGACY_EXECUTABLE}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("仅在组合构建时接入经过哈希校验的同目录字体包", () => {
|
it("仅在组合构建时接入经过哈希校验的同目录字体包", () => {
|
||||||
const builderConfig = readFileSync(
|
const builderConfig = readFileSync(
|
||||||
`${desktopRoot}/electron-builder.config.cjs`,
|
`${desktopRoot}/electron-builder.config.cjs`,
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
|
import { rm } from "node:fs/promises";
|
||||||
|
import { migrateLegacyUserData } from "../src/user-data-migration.js";
|
||||||
|
|
||||||
|
const temporaryDirectories: string[] = [];
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await Promise.all(
|
||||||
|
temporaryDirectories.splice(0).map((directory) =>
|
||||||
|
rm(directory, { recursive: true, force: true })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function createRoot() {
|
||||||
|
const root = await mkdtemp(path.join(tmpdir(), "morphdoc-migration-"));
|
||||||
|
temporaryDirectories.push(root);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("品牌升级用户数据迁移", () => {
|
||||||
|
it("首次启动时复制状态、主题和浏览器本地存储但保留旧目录", async () => {
|
||||||
|
const root = await createRoot();
|
||||||
|
const legacy = path.join(root, "Markdown PDF 导出器");
|
||||||
|
const target = path.join(root, "MorphDoc");
|
||||||
|
await mkdir(path.join(legacy, "themes", "custom"), {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
await mkdir(path.join(legacy, "Local Storage"), {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
await writeFile(
|
||||||
|
path.join(legacy, "window-state.json"),
|
||||||
|
'{"width":1280}',
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
await writeFile(
|
||||||
|
path.join(legacy, "themes", "custom", "theme.css"),
|
||||||
|
"#write{}",
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await migrateLegacyUserData([legacy], target);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
migrated: true,
|
||||||
|
sourceDirectory: legacy,
|
||||||
|
copiedEntries: ["window-state.json", "themes", "Local Storage"]
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
await readFile(path.join(target, "window-state.json"), "utf8")
|
||||||
|
).toBe('{"width":1280}');
|
||||||
|
expect(
|
||||||
|
await readFile(
|
||||||
|
path.join(target, "themes", "custom", "theme.css"),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
).toBe("#write{}");
|
||||||
|
expect(
|
||||||
|
await readFile(path.join(legacy, "window-state.json"), "utf8")
|
||||||
|
).toBe('{"width":1280}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("目标目录已经存在时不覆盖新版本数据", async () => {
|
||||||
|
const root = await createRoot();
|
||||||
|
const legacy = path.join(root, "Markdown PDF 导出器");
|
||||||
|
const target = path.join(root, "MorphDoc");
|
||||||
|
await mkdir(legacy, { recursive: true });
|
||||||
|
await mkdir(target, { recursive: true });
|
||||||
|
await writeFile(path.join(legacy, "window-state.json"), "legacy", "utf8");
|
||||||
|
await writeFile(path.join(target, "window-state.json"), "current", "utf8");
|
||||||
|
|
||||||
|
expect(await migrateLegacyUserData([legacy], target)).toEqual({
|
||||||
|
migrated: false,
|
||||||
|
copiedEntries: []
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
await readFile(path.join(target, "window-state.json"), "utf8")
|
||||||
|
).toBe("current");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -39,7 +39,7 @@ function temporaryDirectories() {
|
|||||||
|
|
||||||
const markdown = `---
|
const markdown = `---
|
||||||
title: Server HTTP DOCX 验收
|
title: Server HTTP DOCX 验收
|
||||||
author: Markdown PDF 导出器研发组
|
author: 墨呈研发组
|
||||||
---
|
---
|
||||||
|
|
||||||
# HTTP 导出保持可编辑
|
# HTTP 导出保持可编辑
|
||||||
|
|||||||
+2
-2
@@ -17,9 +17,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="可配置、可主题化的 Markdown PDF 导出工具"
|
content="面向结构化 Markdown 的主题化文档创作与发布工具"
|
||||||
/>
|
/>
|
||||||
<title>Markdown PDF 导出器</title>
|
<title>墨呈 · MorphDoc</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ import {
|
|||||||
type PdfExportRequest
|
type PdfExportRequest
|
||||||
} from "./pdf-export";
|
} from "./pdf-export";
|
||||||
import { APP_VERSION_LABEL } from "./app-version";
|
import { APP_VERSION_LABEL } from "./app-version";
|
||||||
|
import {
|
||||||
|
APP_DISPLAY_NAME,
|
||||||
|
APP_SLOGAN
|
||||||
|
} from "./app-brand";
|
||||||
import { getSyncedScrollTop } from "./scroll-sync";
|
import { getSyncedScrollTop } from "./scroll-sync";
|
||||||
import {
|
import {
|
||||||
loadPreviewZoom,
|
loadPreviewZoom,
|
||||||
@@ -1487,9 +1491,9 @@ export function App() {
|
|||||||
<header className="topbar">
|
<header className="topbar">
|
||||||
<div className="topbar-primary">
|
<div className="topbar-primary">
|
||||||
<div className="topbar-brand">
|
<div className="topbar-brand">
|
||||||
<p className="eyebrow">内网文档工具</p>
|
<p className="eyebrow">{APP_SLOGAN}</p>
|
||||||
<div className="topbar-brand-title">
|
<div className="topbar-brand-title">
|
||||||
<h1>Markdown PDF 导出器</h1>
|
<h1>{APP_DISPLAY_NAME}</h1>
|
||||||
<span className="app-version">{APP_VERSION_LABEL}</span>
|
<span className="app-version">{APP_VERSION_LABEL}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export const APP_DISPLAY_NAME = __APP_DISPLAY_NAME__;
|
||||||
|
export const APP_WINDOW_TITLE = __APP_WINDOW_TITLE__;
|
||||||
|
export const APP_SLOGAN = __APP_SLOGAN__;
|
||||||
|
export const APP_DESCRIPTION = __APP_DESCRIPTION__;
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { App } from "./App";
|
import { App } from "./App";
|
||||||
|
import { APP_WINDOW_TITLE } from "./app-brand";
|
||||||
|
import { APP_VERSION_LABEL } from "./app-version";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
|
document.title = `${APP_WINDOW_TITLE} ${APP_VERSION_LABEL}`;
|
||||||
|
|
||||||
const rootElement = document.getElementById("root");
|
const rootElement = document.getElementById("root");
|
||||||
|
|
||||||
if (!rootElement) {
|
if (!rootElement) {
|
||||||
|
|||||||
Vendored
+4
@@ -16,6 +16,10 @@ import type {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
const __APP_VERSION__: string;
|
const __APP_VERSION__: string;
|
||||||
|
const __APP_DISPLAY_NAME__: string;
|
||||||
|
const __APP_WINDOW_TITLE__: string;
|
||||||
|
const __APP_SLOGAN__: string;
|
||||||
|
const __APP_DESCRIPTION__: string;
|
||||||
|
|
||||||
interface DesktopPdfResult {
|
interface DesktopPdfResult {
|
||||||
pdf: Uint8Array;
|
pdf: Uint8Array;
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
APP_DESCRIPTION,
|
||||||
|
APP_DISPLAY_NAME,
|
||||||
|
APP_SLOGAN,
|
||||||
|
APP_WINDOW_TITLE
|
||||||
|
} from "../src/app-brand";
|
||||||
|
|
||||||
|
const webRoot = fileURLToPath(new URL("../", import.meta.url));
|
||||||
|
|
||||||
|
describe("产品品牌", () => {
|
||||||
|
it("在主界面使用中文名和产品标语", () => {
|
||||||
|
expect(APP_DISPLAY_NAME).toBe("墨呈");
|
||||||
|
expect(APP_SLOGAN).toBe("一稿多式,即写即呈");
|
||||||
|
expect(APP_WINDOW_TITLE).toBe("墨呈 · MorphDoc");
|
||||||
|
expect(APP_DESCRIPTION).toBe(
|
||||||
|
"面向结构化 Markdown 的主题化文档创作与发布工具"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("在静态入口中移除旧工具名称", () => {
|
||||||
|
const indexHtml = readFileSync(`${webRoot}/index.html`, "utf8");
|
||||||
|
const mainSource = readFileSync(`${webRoot}/src/main.tsx`, "utf8");
|
||||||
|
|
||||||
|
expect(indexHtml).toContain("<title>墨呈 · MorphDoc</title>");
|
||||||
|
expect(mainSource).toContain(
|
||||||
|
"document.title = `${APP_WINDOW_TITLE} ${APP_VERSION_LABEL}`"
|
||||||
|
);
|
||||||
|
expect(indexHtml).not.toContain("Markdown PDF 导出器");
|
||||||
|
expect(indexHtml).not.toContain("内网文档工具");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,8 +10,8 @@ const webRoot = fileURLToPath(new URL("../", import.meta.url));
|
|||||||
|
|
||||||
describe("应用版本", () => {
|
describe("应用版本", () => {
|
||||||
it("从统一构建版本生成标题徽标", () => {
|
it("从统一构建版本生成标题徽标", () => {
|
||||||
expect(APP_VERSION).toBe("0.5.1");
|
expect(APP_VERSION).toBe("0.6.0");
|
||||||
expect(APP_VERSION_LABEL).toBe("v0.5.1");
|
expect(APP_VERSION_LABEL).toBe("v0.6.0");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("允许浏览器加载同源 Web Manifest", () => {
|
it("允许浏览器加载同源 Web Manifest", () => {
|
||||||
|
|||||||
+16
-1
@@ -9,6 +9,17 @@ const rootPackage = JSON.parse(
|
|||||||
"utf8"
|
"utf8"
|
||||||
)
|
)
|
||||||
) as { version: string };
|
) as { version: string };
|
||||||
|
const productBrand = JSON.parse(
|
||||||
|
readFileSync(
|
||||||
|
fileURLToPath(new URL("../../product.json", import.meta.url)),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
) as {
|
||||||
|
displayName: string;
|
||||||
|
windowTitle: string;
|
||||||
|
slogan: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
@@ -16,7 +27,11 @@ export default defineConfig({
|
|||||||
new URL("../../logos/web", import.meta.url)
|
new URL("../../logos/web", import.meta.url)
|
||||||
),
|
),
|
||||||
define: {
|
define: {
|
||||||
__APP_VERSION__: JSON.stringify(rootPackage.version)
|
__APP_VERSION__: JSON.stringify(rootPackage.version),
|
||||||
|
__APP_DISPLAY_NAME__: JSON.stringify(productBrand.displayName),
|
||||||
|
__APP_WINDOW_TITLE__: JSON.stringify(productBrand.windowTitle),
|
||||||
|
__APP_SLOGAN__: JSON.stringify(productBrand.slogan),
|
||||||
|
__APP_DESCRIPTION__: JSON.stringify(productBrand.description)
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
|||||||
+10
-2
@@ -1,6 +1,6 @@
|
|||||||
# Markdown PDF 导出器进度
|
# 墨呈进度
|
||||||
|
|
||||||
最后更新:2026-08-01
|
最后更新:2026-08-02
|
||||||
|
|
||||||
## 1. 当前概况
|
## 1. 当前概况
|
||||||
|
|
||||||
@@ -77,6 +77,12 @@ Windows ZIP 和 Linux amd64 tarball 的 SHA-256 均已核验;Linux 静态程
|
|||||||
复制主题允许继续用于公司内部源码共享、部署和安装包分发,但不受项目
|
复制主题允许继续用于公司内部源码共享、部署和安装包分发,但不受项目
|
||||||
许可证重新授权;公开 GitHub 前必须移除、替换或取得公开分发授权。
|
许可证重新授权;公开 GitHub 前必须移除、替换或取得公开分发授权。
|
||||||
|
|
||||||
|
`v0.6.0` 发布收口阶段 A 已完成品牌与版本迁移:用户界面中文名为
|
||||||
|
“墨呈”,产品标语为“一稿多式,即写即呈”,窗口标题为
|
||||||
|
“墨呈 · MorphDoc v0.6.0”;Desktop 安装目录、可执行文件与发行产物统一
|
||||||
|
使用 `MorphDoc`。Electron `appId` 和 Markdown 文件关联 ProgID 保持不变,
|
||||||
|
并为旧版用户数据、快捷方式和应用注册项提供兼容迁移与清理。
|
||||||
|
|
||||||
`v0.6.0` 阶段 3 已完成 DOCX 共享协议:`packages/core` 统一定义请求
|
`v0.6.0` 阶段 3 已完成 DOCX 共享协议:`packages/core` 统一定义请求
|
||||||
Schema、资源、Pandoc capability、错误码、结果、诊断、耗时、MIME 和
|
Schema、资源、Pandoc capability、错误码、结果、诊断、耗时、MIME 和
|
||||||
安全文件名;`packages/application` 新增 `prepareDocxExport()`,复用
|
安全文件名;`packages/application` 新增 `prepareDocxExport()`,复用
|
||||||
@@ -1412,6 +1418,8 @@ ECharts 第二阶段浏览器与 PDF 验证结果:
|
|||||||
- 阶段 12D-R4 布局视觉矩阵:已完成六组主题、纸张、方向与页边距组合的
|
- 阶段 12D-R4 布局视觉矩阵:已完成六组主题、纸张、方向与页边距组合的
|
||||||
Chromium/Word/WPS 元素级严格门禁,并重跑 14 套主题生产矩阵;分页流
|
Chromium/Word/WPS 元素级严格门禁,并重跑 14 套主题生产矩阵;分页流
|
||||||
差异保留为诊断项,不再误判为样式失败;
|
差异保留为诊断项,不再误判为样式失败;
|
||||||
|
- 发布收口阶段 A:已完成 v0.6.0 版本、墨呈/MorphDoc 品牌、NSIS 名称、
|
||||||
|
文件关联兼容和旧版用户数据迁移;
|
||||||
- 阶段 13:完成 Word/WPS 双向互存、外部主题兼容、体积和正式发布验收。
|
- 阶段 13:完成 Word/WPS 双向互存、外部主题兼容、体积和正式发布验收。
|
||||||
|
|
||||||
每个阶段验收通过后创建一个独立提交,再进入下一阶段。当前阶段不得混入
|
每个阶段验收通过后创建一个独立提交,再进入下一阶段。当前阶段不得混入
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ SetCompressorDictSize 64
|
|||||||
!define MANAGER_ROOT "$LOCALAPPDATA\md-to-pdf\font-pack-installers"
|
!define MANAGER_ROOT "$LOCALAPPDATA\md-to-pdf\font-pack-installers"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
Name "Markdown PDF 导出器字体包 - ${PACK_NAME}"
|
Name "墨呈字体包 - ${PACK_NAME}"
|
||||||
OutFile "${OUTPUT_FILE}"
|
OutFile "${OUTPUT_FILE}"
|
||||||
InstallDir "${MANAGER_ROOT}\${PACK_ID}"
|
InstallDir "${MANAGER_ROOT}\${PACK_ID}"
|
||||||
ShowInstDetails show
|
ShowInstDetails show
|
||||||
ShowUninstDetails show
|
ShowUninstDetails show
|
||||||
BrandingText "Markdown PDF 导出器"
|
BrandingText "墨呈"
|
||||||
|
|
||||||
!ifdef ICON_FILE
|
!ifdef ICON_FILE
|
||||||
Icon "${ICON_FILE}"
|
Icon "${ICON_FILE}"
|
||||||
@@ -55,7 +55,7 @@ BrandingText "Markdown PDF 导出器"
|
|||||||
!insertmacro MUI_LANGUAGE "SimpChinese"
|
!insertmacro MUI_LANGUAGE "SimpChinese"
|
||||||
|
|
||||||
VIProductVersion "1.0.0.0"
|
VIProductVersion "1.0.0.0"
|
||||||
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "ProductName" "Markdown PDF 导出器字体包"
|
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "ProductName" "墨呈字体包"
|
||||||
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileDescription" "${PACK_NAME}"
|
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileDescription" "${PACK_NAME}"
|
||||||
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileVersion" "${PACK_VERSION}"
|
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileVersion" "${PACK_VERSION}"
|
||||||
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "LegalCopyright" "Copyright © YIXIONG Tech.ltd"
|
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "LegalCopyright" "Copyright © YIXIONG Tech.ltd"
|
||||||
@@ -96,7 +96,7 @@ Section "安装字体包" SEC_FONT_PACK
|
|||||||
FileClose $R3
|
FileClose $R3
|
||||||
|
|
||||||
!ifdef REGISTER_UNINSTALL
|
!ifdef REGISTER_UNINSTALL
|
||||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "DisplayName" "Markdown PDF 导出器字体包 - ${PACK_NAME}"
|
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "DisplayName" "墨呈字体包 - ${PACK_NAME}"
|
||||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "DisplayVersion" "${PACK_VERSION}"
|
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "DisplayVersion" "${PACK_VERSION}"
|
||||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "Publisher" "YIXIONG Tech.ltd"
|
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "Publisher" "YIXIONG Tech.ltd"
|
||||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "InstallLocation" "$INSTDIR"
|
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\md-to-pdf-font-pack-${PACK_ID}" "InstallLocation" "$INSTDIR"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Markdown PDF 导出器",
|
"name": "墨呈 · MorphDoc",
|
||||||
"short_name": "Markdown PDF",
|
"short_name": "墨呈",
|
||||||
"start_url": "./",
|
"start_url": "./",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#f4f2ed",
|
"background_color": "#f4f2ed",
|
||||||
|
|||||||
Generated
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "md-to-pdf",
|
"name": "md-to-pdf",
|
||||||
"version": "0.5.1",
|
"version": "0.6.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "md-to-pdf",
|
"name": "md-to-pdf",
|
||||||
"version": "0.5.1",
|
"version": "0.6.0",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"apps/desktop": {
|
"apps/desktop": {
|
||||||
"name": "@md-to-pdf/desktop",
|
"name": "@md-to-pdf/desktop",
|
||||||
"version": "0.5.1",
|
"version": "0.6.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@md-to-pdf/application": "0.4.1",
|
"@md-to-pdf/application": "0.4.1",
|
||||||
"@md-to-pdf/docx-engine": "0.1.0",
|
"@md-to-pdf/docx-engine": "0.1.0",
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "md-to-pdf",
|
"name": "md-to-pdf",
|
||||||
"version": "0.5.1",
|
"version": "0.6.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"description": "可配置、可主题化的 Markdown PDF 导出工具",
|
"description": "面向结构化 Markdown 的主题化文档创作与发布工具",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: v0.6.0 DOCX 自动验收
|
title: v0.6.0 DOCX 自动验收
|
||||||
author: Markdown PDF 导出器研发组
|
author: 墨呈研发组
|
||||||
subject: Word 与 WPS 可编辑性验收
|
subject: Word 与 WPS 可编辑性验收
|
||||||
keywords: [DOCX, Pandoc, OOXML]
|
keywords: [DOCX, Pandoc, OOXML]
|
||||||
lang: zh-CN
|
lang: zh-CN
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: DOCX 媒体视觉专项验收
|
title: DOCX 媒体视觉专项验收
|
||||||
author: Markdown PDF 导出器研发组
|
author: 墨呈研发组
|
||||||
lang: zh-CN
|
lang: zh-CN
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Pandoc 媒体转换验收
|
title: Pandoc 媒体转换验收
|
||||||
author: Markdown PDF 导出器研发组
|
author: 墨呈研发组
|
||||||
---
|
---
|
||||||
|
|
||||||
# 可编辑正文
|
# 可编辑正文
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 阶段六动态模板验收
|
title: 阶段六动态模板验收
|
||||||
author: Markdown PDF 导出器研发组
|
author: 墨呈研发组
|
||||||
---
|
---
|
||||||
|
|
||||||
# 一级标题
|
# 一级标题
|
||||||
|
|||||||
@@ -406,7 +406,7 @@ for (const variant of variants) {
|
|||||||
const fileName = `${variant.id}.md`;
|
const fileName = `${variant.id}.md`;
|
||||||
const metadata = {
|
const metadata = {
|
||||||
title: "v0.6.0 DOCX 自动验收",
|
title: "v0.6.0 DOCX 自动验收",
|
||||||
author: "Markdown PDF 导出器研发组",
|
author: "墨呈研发组",
|
||||||
subject: "Word 与 WPS 可编辑性验收",
|
subject: "Word 与 WPS 可编辑性验收",
|
||||||
keywords: ["DOCX", "Pandoc", "OOXML"],
|
keywords: ["DOCX", "Pandoc", "OOXML"],
|
||||||
language: "zh-CN"
|
language: "zh-CN"
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const conversionInput = {
|
|||||||
themeTokens,
|
themeTokens,
|
||||||
metadata: {
|
metadata: {
|
||||||
title: "Pandoc 媒体转换验收",
|
title: "Pandoc 媒体转换验收",
|
||||||
author: "Markdown PDF 导出器研发组",
|
author: "墨呈研发组",
|
||||||
subject: "",
|
subject: "",
|
||||||
keywords: [],
|
keywords: [],
|
||||||
language: "zh-CN"
|
language: "zh-CN"
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ function createVariant(
|
|||||||
fileName: `${variant.id}.md`,
|
fileName: `${variant.id}.md`,
|
||||||
metadata: {
|
metadata: {
|
||||||
title: "阶段六动态模板验收",
|
title: "阶段六动态模板验收",
|
||||||
author: "Markdown PDF 导出器研发组"
|
author: "墨呈研发组"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const referencePath = path.join(
|
const referencePath = path.join(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# @md-to-pdf/preview-engine
|
# @md-to-pdf/preview-engine
|
||||||
|
|
||||||
Markdown PDF 导出器的共享预览与分页引擎。Web 连续/快速预览、服务端
|
墨呈的共享预览与分页引擎。Web 连续/快速预览、服务端
|
||||||
Playwright PDF 和 Electron PDF 均通过同一套运行时完成媒体渲染、尺寸
|
Playwright PDF 和 Electron PDF 均通过同一套运行时完成媒体渲染、尺寸
|
||||||
适配与 Paged.js 分页。
|
适配与 Paged.js 分页。
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"appId": "com.md-to-pdf.desktop",
|
||||||
|
"englishName": "MorphDoc",
|
||||||
|
"displayName": "墨呈",
|
||||||
|
"windowTitle": "墨呈 · MorphDoc",
|
||||||
|
"slogan": "一稿多式,即写即呈",
|
||||||
|
"description": "面向结构化 Markdown 的主题化文档创作与发布工具",
|
||||||
|
"executableName": "MorphDoc",
|
||||||
|
"artifactPrefix": "MorphDoc",
|
||||||
|
"legacyDisplayName": "Markdown PDF 导出器",
|
||||||
|
"legacyExecutableName": "md-to-pdf"
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Markdown ECharts 图表教程
|
title: Markdown ECharts 图表教程
|
||||||
author: Markdown PDF 导出器
|
author: 墨呈
|
||||||
keywords: [ECharts, Markdown, 图表, 教程]
|
keywords: [ECharts, Markdown, 图表, 教程]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 公文与正式文档主题使用教程
|
title: 公文与正式文档主题使用教程
|
||||||
author: Markdown PDF 导出器
|
author: 墨呈
|
||||||
keywords: [公文, 红头文件, 正式文档, 标书, 教程]
|
keywords: [公文, 红头文件, 正式文档, 标书, 教程]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user