release: 发布 v0.5.1
新增能力:内置 4 套红头、3 套正式文档和 3 套标书主题,支持主题推荐页边距、页面装饰、页眉页脚和页码;增加结构化公文、项目报告及标书 Front Matter,内置 Fandol 中文字体、两份教程和 14 份主题示例。 桌面工作流:重组更多菜单,增加新建、保存、另存为快捷键和未保存确认;另存为后跟随新路径,主题示例自动切换主题,Desktop 发行包完整携带教程、示例与字体。 问题修复:修正红头标题居中与正式文档字体;围栏代码按正文宽度自动换行,长内容安全断行,至少两行即可在当前页分页,并统一保留 8px 左侧内容留白;Docker Web 镜像正确打包 samples。 兼容与部署:未声明主题推荐设置时继续使用 16mm 默认页边距;Web 隐藏不适用的另存为。正式镜像 yixiong/md-to-pdf:v0.5.1 内容 ID 为 sha256:72f519a69bfd6cb2f6df30c2be938e58ceb6f20e4748a32551874de3d436b276,Compose 健康运行。 验证结果:最终修复前 65 个测试文件、286 项测试通过,最终代码块与主题定向测试 27 项通过;全项目类型检查、生产构建和 git diff --check 通过。容器检出 14 套主题、17 份 Markdown,代码块回归 PDF 为 2 页且无越界。NSIS SHA-256 为 676CCE73D782B0B15AC6BC68F253CFBC4740383583E4DAA98F746EDF9999C5CC,ZIP SHA-256 为 82BF6ADF1200604F145CC86FA3ED193955CF6741EBEE3DF8953483515CFF621F。
This commit is contained in:
+121
-14
@@ -4,8 +4,10 @@ import {
|
||||
type ExportConfig
|
||||
} from "@md-to-pdf/core";
|
||||
|
||||
export const EXPORT_CONFIG_STORAGE_KEY = "md-to-pdf.export-config.v3";
|
||||
export const LEGACY_EXPORT_CONFIG_STORAGE_KEY =
|
||||
export const EXPORT_CONFIG_STORAGE_KEY = "md-to-pdf.export-config.v4";
|
||||
export const LEGACY_EXPORT_CONFIG_V3_STORAGE_KEY =
|
||||
"md-to-pdf.export-config.v3";
|
||||
export const LEGACY_EXPORT_CONFIG_V2_STORAGE_KEY =
|
||||
"md-to-pdf.export-config.v2";
|
||||
|
||||
type SettingsStorage = Pick<Storage, "getItem" | "setItem">;
|
||||
@@ -14,6 +16,119 @@ function cloneDefaultExportConfig() {
|
||||
return structuredClone(defaultExportConfig);
|
||||
}
|
||||
|
||||
function pickLegacyPageDecorations(value: unknown) {
|
||||
if (typeof value !== "object" || value === null) {
|
||||
return value;
|
||||
}
|
||||
const source = value as Record<string, unknown>;
|
||||
if ("left" in source) {
|
||||
return {
|
||||
enabled: source.enabled,
|
||||
height: source.height,
|
||||
showDivider: source.showDivider,
|
||||
fontSize: source.fontSize,
|
||||
color: source.color,
|
||||
left: source.left,
|
||||
center: source.center,
|
||||
right: source.right
|
||||
};
|
||||
}
|
||||
return {
|
||||
enabled: source.enabled,
|
||||
height: source.height,
|
||||
showDivider: source.showDivider,
|
||||
fontSize: source.fontSize,
|
||||
color: source.color,
|
||||
alignment: source.alignment,
|
||||
format: source.format,
|
||||
template: source.template,
|
||||
startFrom: source.startFrom
|
||||
};
|
||||
}
|
||||
|
||||
function usesLegacyPageDecorationDefaults(
|
||||
header: unknown,
|
||||
footer: unknown
|
||||
) {
|
||||
return (
|
||||
JSON.stringify(pickLegacyPageDecorations(header)) ===
|
||||
JSON.stringify(
|
||||
pickLegacyPageDecorations(defaultExportConfig.header)
|
||||
) &&
|
||||
JSON.stringify(pickLegacyPageDecorations(footer)) ===
|
||||
JSON.stringify(
|
||||
pickLegacyPageDecorations(defaultExportConfig.footer)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function migrateStoredExportConfig(value: unknown): unknown {
|
||||
if (
|
||||
typeof value !== "object" ||
|
||||
value === null ||
|
||||
!("version" in value) ||
|
||||
!("paper" in value)
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (
|
||||
value.version !== 2 &&
|
||||
value.version !== 3 &&
|
||||
value.version !== defaultExportConfig.version
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const source = value as Record<string, unknown>;
|
||||
const hasPageDecorationsMode =
|
||||
typeof source.pageDecorationsMode === "string";
|
||||
|
||||
const paper =
|
||||
typeof value.paper === "object" && value.paper !== null
|
||||
? value.paper
|
||||
: {};
|
||||
const margins =
|
||||
"margins" in paper &&
|
||||
typeof paper.margins === "object" &&
|
||||
paper.margins !== null
|
||||
? paper.margins
|
||||
: {};
|
||||
const usesLegacyDefaults =
|
||||
JSON.stringify(margins) ===
|
||||
JSON.stringify(defaultExportConfig.paper.margins);
|
||||
const hasMarginMode =
|
||||
typeof (paper as Record<string, unknown>).marginMode === "string";
|
||||
|
||||
return {
|
||||
...value,
|
||||
...(value.version === 2 || value.version === 3
|
||||
? { version: defaultExportConfig.version }
|
||||
: {}),
|
||||
...(value.version === 2
|
||||
? { mermaid: structuredClone(defaultExportConfig.mermaid) }
|
||||
: {}),
|
||||
...(!hasPageDecorationsMode
|
||||
? {
|
||||
pageDecorationsMode: usesLegacyPageDecorationDefaults(
|
||||
source.header,
|
||||
source.footer
|
||||
)
|
||||
? "theme"
|
||||
: "custom"
|
||||
}
|
||||
: {}),
|
||||
paper: {
|
||||
...paper,
|
||||
...(!hasMarginMode
|
||||
? {
|
||||
marginMode: usesLegacyDefaults ? "theme" : "custom"
|
||||
}
|
||||
: {})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function parseStoredExportConfig(rawValue: string | null): ExportConfig {
|
||||
if (!rawValue) {
|
||||
return cloneDefaultExportConfig();
|
||||
@@ -21,17 +136,7 @@ export function parseStoredExportConfig(rawValue: string | null): ExportConfig {
|
||||
|
||||
try {
|
||||
const value: unknown = JSON.parse(rawValue);
|
||||
const migratedValue =
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
"version" in value &&
|
||||
value.version === 2
|
||||
? {
|
||||
...value,
|
||||
version: defaultExportConfig.version,
|
||||
mermaid: structuredClone(defaultExportConfig.mermaid)
|
||||
}
|
||||
: value;
|
||||
const migratedValue = migrateStoredExportConfig(value);
|
||||
const parsed = exportConfigSchema.safeParse(migratedValue);
|
||||
return parsed.success ? parsed.data : cloneDefaultExportConfig();
|
||||
} catch {
|
||||
@@ -47,7 +152,9 @@ export function loadExportConfig(
|
||||
return parseStoredExportConfig(currentValue);
|
||||
}
|
||||
|
||||
const legacyValue = storage.getItem(LEGACY_EXPORT_CONFIG_STORAGE_KEY);
|
||||
const legacyValue =
|
||||
storage.getItem(LEGACY_EXPORT_CONFIG_V3_STORAGE_KEY) ??
|
||||
storage.getItem(LEGACY_EXPORT_CONFIG_V2_STORAGE_KEY);
|
||||
const config = parseStoredExportConfig(legacyValue);
|
||||
if (legacyValue) {
|
||||
storage.setItem(EXPORT_CONFIG_STORAGE_KEY, JSON.stringify(config));
|
||||
|
||||
Reference in New Issue
Block a user