Files
MorphDoc/packages/application/src/docx-theme-token-service.ts
T
SkyJourney 2c5c1bd317 release: 发布 v0.6.1 DOCX 视觉一致性修复
新增通用 CSS 到 OOXML 翻译修复,统一字体、字距、精确行距、段落、列表、表格、引用、代码块与行内代码连续性,不引入按主题 ID 分支。

新增 MdTP Mono 并统一 Serif、Sans、Mono 三字体包的 Chromium 与 DOCX 使用链;字体声明、嵌入部件和 Word/WPS 实际采用均进入硬门禁。

重建封面整页及正文语义块视觉差分,14 套主题、纵横两个方向、五组页边距共 140 个真实场景全部通过,阻断失败和诊断失败均为零。

源码服务、Docker Web API 与实际安装 Desktop 的 red-briefing 导出均包含 5 个字体部件;Word/WPS 原生渲染和逐页复核通过。修复 Docker 构建上下文与运行层复用软链接,并完善 v0.6.1 版本、发行说明和发布归集。

验证:npm test(116 个文件、616 项测试)、npm run typecheck、npm run build、git diff --check 全部通过。Desktop 安装器与 ZIP、Docker v0.6.1 镜像已生成;Windows 产物仍为未签名内部发行。
2026-08-04 10:30:44 +08:00

107 lines
2.6 KiB
TypeScript

import type {
DocxThemeTokenSet,
DocxThemeStyleCaptureAdapter
} from "@md-to-pdf/docx-theme-engine";
import {
DOCX_STYLE_SLOTS,
DocxThemeStyleSnapshotCache,
createDocxThemeStyleFingerprint,
normalizeDocxThemeMappingConfig,
resolveDocxThemeTokens,
type DocxThemeStyleSnapshot
} from "@md-to-pdf/docx-theme-engine";
import type { PreparedDocxExport } from "./application-service.js";
import { SHARED_HIGHLIGHT_CSS } from "@md-to-pdf/preview-engine";
export interface PreparedDocxThemeTokens {
tokens: DocxThemeTokenSet;
warnings: string[];
}
export interface DocxThemeTokenProvider {
prepare(
prepared: PreparedDocxExport,
signal?: AbortSignal
): Promise<PreparedDocxThemeTokens>;
}
function createExplicitSnapshot(
themeId: string,
themeFingerprint: string
): DocxThemeStyleSnapshot {
return {
schemaVersion: 1,
themeId,
themeFingerprint,
viewport: {
widthPx: 794,
heightPx: 1123,
deviceScaleFactor: 1
},
rootFontSizePx: 16,
slots: DOCX_STYLE_SLOTS.map(({ name }) => ({
slot: name,
matched: false
}))
};
}
export class DocxThemeTokenService
implements DocxThemeTokenProvider
{
private readonly snapshots: DocxThemeStyleSnapshotCache;
constructor(
private readonly adapter: DocxThemeStyleCaptureAdapter
) {
this.snapshots = new DocxThemeStyleSnapshotCache(adapter);
}
async prepare(
prepared: PreparedDocxExport,
signal?: AbortSignal
): Promise<PreparedDocxThemeTokens> {
signal?.throwIfAborted();
const config = normalizeDocxThemeMappingConfig(
prepared.theme.manifest
);
const themeFingerprint =
await createDocxThemeStyleFingerprint(
prepared.theme.manifest.id,
`${SHARED_HIGHLIGHT_CSS}\n${prepared.theme.css}`
);
signal?.throwIfAborted();
const snapshot =
config.mode === "explicit"
? createExplicitSnapshot(
prepared.theme.manifest.id,
themeFingerprint
)
: await this.snapshots.capture(
{
themeId: prepared.theme.manifest.id,
themeFingerprint,
themeCss: `${SHARED_HIGHLIGHT_CSS}\n${prepared.theme.css}`,
baseUrl: this.adapter.themeStyleBaseUrl
},
signal
);
const tokens = resolveDocxThemeTokens({
snapshot,
config
});
return {
tokens,
warnings: tokens.diagnostics
.filter(
(diagnostic) => diagnostic.severity !== "info"
)
.map((diagnostic) => diagnostic.message)
};
}
invalidate(themeId?: string) {
this.snapshots.invalidate(themeId);
}
}