Files
MorphDoc/apps/web/src/export-settings.ts
T
SkyJourney 83e6559c2c 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。
2026-07-29 16:58:17 +08:00

176 lines
4.5 KiB
TypeScript

import {
defaultExportConfig,
exportConfigSchema,
type ExportConfig
} from "@md-to-pdf/core";
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">;
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();
}
try {
const value: unknown = JSON.parse(rawValue);
const migratedValue = migrateStoredExportConfig(value);
const parsed = exportConfigSchema.safeParse(migratedValue);
return parsed.success ? parsed.data : cloneDefaultExportConfig();
} catch {
return cloneDefaultExportConfig();
}
}
export function loadExportConfig(
storage: SettingsStorage = window.localStorage
) {
const currentValue = storage.getItem(EXPORT_CONFIG_STORAGE_KEY);
if (currentValue) {
return parseStoredExportConfig(currentValue);
}
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));
}
return config;
}
export function saveExportConfig(
config: ExportConfig,
storage: SettingsStorage = window.localStorage
) {
const parsed = exportConfigSchema.parse(config);
storage.setItem(EXPORT_CONFIG_STORAGE_KEY, JSON.stringify(parsed));
}
export function resetExportConfig() {
return cloneDefaultExportConfig();
}