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 产物仍为未签名内部发行。
This commit is contained in:
SkyJourney
2026-08-04 10:30:44 +08:00
parent b275c671fc
commit 2c5c1bd317
84 changed files with 4404 additions and 344 deletions
+72 -30
View File
@@ -442,34 +442,32 @@ function normalizeComputedSlot(
style.spacingAfterPt = spacingAfterPt;
}
}
if (context.kind !== "inline") {
const padding = {
top: length(context, "padding-top", computed.paddingTop, {
nonNegative: true
}),
right: length(context, "padding-right", computed.paddingRight, {
nonNegative: true
}),
bottom: length(context, "padding-bottom", computed.paddingBottom, {
nonNegative: true
}),
left: length(context, "padding-left", computed.paddingLeft, {
nonNegative: true
})
const padding = {
top: length(context, "padding-top", computed.paddingTop, {
nonNegative: true
}),
right: length(context, "padding-right", computed.paddingRight, {
nonNegative: true
}),
bottom: length(context, "padding-bottom", computed.paddingBottom, {
nonNegative: true
}),
left: length(context, "padding-left", computed.paddingLeft, {
nonNegative: true
})
};
if (
padding.top !== undefined &&
padding.right !== undefined &&
padding.bottom !== undefined &&
padding.left !== undefined
) {
style.paddingPt = {
top: padding.top,
right: padding.right,
bottom: padding.bottom,
left: padding.left
};
if (
padding.top !== undefined &&
padding.right !== undefined &&
padding.bottom !== undefined &&
padding.left !== undefined
) {
style.paddingPt = {
top: padding.top,
right: padding.right,
bottom: padding.bottom,
left: padding.left
};
}
}
const containingBlockWidthPt = computed.containingBlockWidth
? parseCssLengthToPt(computed.containingBlockWidth)
@@ -652,7 +650,8 @@ function applyBorderColor(
function applyOverrides(
slot: DocxStyleSlotName,
styleInput: DocxSlotStyleToken,
overrides: DocxThemeStyleOverrides
overrides: DocxThemeStyleOverrides,
replaceExisting: boolean
): {
style: DocxSlotStyleToken;
applied: boolean;
@@ -663,7 +662,10 @@ function applyOverrides(
key: Key,
value: MutableStyle[Key] | undefined
) => {
if (value !== undefined) {
if (
value !== undefined &&
(replaceExisting || style[key] === undefined)
) {
style[key] = value;
applied = true;
}
@@ -887,7 +889,10 @@ export function resolveDocxThemeTokens(
const override = applyOverrides(
definition.name,
resolved.style,
input.config.overrides
input.config.overrides,
input.config.mode === "explicit" ||
!input.config.legacyPreset ||
resolved.source === "preset-fallback"
);
if (override.applied) {
diagnostics.push({
@@ -907,6 +912,43 @@ export function resolveDocxThemeTokens(
return resolved;
}
);
const blockQuote = slots.find((entry) => entry.slot === "block-quote");
const blockQuoteText = slots.find(
(entry) => entry.slot === "block-quote-text"
);
if (blockQuote && blockQuoteText) {
const text = blockQuoteText.style;
blockQuote.style = {
...blockQuote.style,
...(text.fontCandidates.length > 0
? { fontCandidates: text.fontCandidates }
: {}),
...(text.fontSizePt !== undefined
? { fontSizePt: text.fontSizePt }
: {}),
...(text.bold !== undefined ? { bold: text.bold } : {}),
...(text.italic !== undefined ? { italic: text.italic } : {}),
...(text.underline !== undefined
? { underline: text.underline }
: {}),
...(text.strikethrough !== undefined
? { strikethrough: text.strikethrough }
: {}),
...(text.color !== undefined ? { color: text.color } : {}),
...(text.lineSpacing !== undefined
? { lineSpacing: text.lineSpacing }
: {}),
...(text.letterSpacingPt !== undefined
? { letterSpacingPt: text.letterSpacingPt }
: {}),
...(text.alignment !== undefined
? { alignment: text.alignment }
: {}),
...(text.firstLineIndentPt !== undefined
? { firstLineIndentPt: text.firstLineIndentPt }
: {})
};
}
return docxThemeTokenSetSchema.parse({
schemaVersion: 1,
themeId: snapshot.themeId,
+3 -3
View File
@@ -34,8 +34,8 @@ export function createDocxStyleProbeMarkup(): string {
</p>
<ul ${attribute("unordered-list")}><li ${attribute("list-item")}>无序列表</li></ul>
<ol ${attribute("ordered-list")}><li>有序列表</li></ol>
<blockquote ${attribute("block-quote")}><p>引用内容</p></blockquote>
<pre class="md-fences" ${attribute("code-block")}><code>const value = 1;</code></pre>
<blockquote ${attribute("block-quote")}><p ${attribute("block-quote-text")}>引用内容</p></blockquote>
<pre class="md-fences" ${attribute("code-block")}><code class="language-javascript" ${attribute("code-block-text")}><span class="hljs-keyword" ${attribute("code-token-keyword")}>const</span> <span class="hljs-attr" ${attribute("code-token-attribute")}>key</span> <span class="hljs-operator" ${attribute("code-token-operator")}>=</span> <span class="hljs-string" ${attribute("code-token-string")}>&quot;value&quot;</span>; <span class="hljs-literal" ${attribute("code-token-literal")}>true</span> <span class="hljs-title" ${attribute("code-token-title")}>title</span> <span class="hljs-number" ${attribute("code-token-number")}>1</span> <span class="hljs-comment" ${attribute("code-token-comment")}>// comment</span> <span class="hljs-built_in" ${attribute("code-token-built-in")}>Array</span> <span class="hljs-meta" ${attribute("code-token-meta")}>@meta</span> <span class="hljs-variable" ${attribute("code-token-variable")}>value</span> <span class="hljs-regexp" ${attribute("code-token-regexp")}>/x/</span> <span class="hljs-punctuation" ${attribute("code-token-punctuation")}>{}</span></code></pre>
<table ${attribute("table")}>
<thead><tr><th ${attribute("table-header")}>表头</th></tr></thead>
<tbody><tr><td ${attribute("table-cell")}>单元格</td></tr></tbody>
@@ -61,7 +61,7 @@ export function createDocxStyleProbeMarkup(): string {
</section>
<section class="docx-probe-briefing">
<header class="doc-masthead doc-masthead-briefing" ${attribute("briefing-masthead")}>
<p class="doc-briefing-masthead">工作简报</p>
<p class="doc-briefing-masthead" ${attribute("briefing-masthead-text")}>工作简报</p>
<div class="doc-briefing-meta" ${attribute("briefing-meta")}><span>第1期</span><span>示例单位</span><time>2026年7月30日</time></div>
</header>
<h1 class="doc-title" ${attribute("briefing-title")}>简报标题</h1>
@@ -118,7 +118,6 @@ body {
border-radius: 0;
background: transparent;
box-shadow: none;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
line-height: inherit;
+32
View File
@@ -19,7 +19,22 @@ export const DOCX_STYLE_SLOT_NAMES = [
"ordered-list",
"list-item",
"block-quote",
"block-quote-text",
"code-block",
"code-block-text",
"code-token-keyword",
"code-token-literal",
"code-token-string",
"code-token-attribute",
"code-token-title",
"code-token-number",
"code-token-comment",
"code-token-built-in",
"code-token-meta",
"code-token-variable",
"code-token-operator",
"code-token-regexp",
"code-token-punctuation",
"table",
"table-header",
"table-cell",
@@ -42,6 +57,7 @@ export const DOCX_STYLE_SLOT_NAMES = [
"official-copy-to",
"official-printing-row",
"briefing-masthead",
"briefing-masthead-text",
"briefing-meta",
"briefing-title",
"briefing-contact",
@@ -121,7 +137,22 @@ const kinds: Record<DocxStyleSlotName, DocxStyleSlotKind> = {
"ordered-list": "list",
"list-item": "list",
"block-quote": "paragraph",
"block-quote-text": "paragraph",
"code-block": "paragraph",
"code-block-text": "inline",
"code-token-keyword": "inline",
"code-token-literal": "inline",
"code-token-string": "inline",
"code-token-attribute": "inline",
"code-token-title": "inline",
"code-token-number": "inline",
"code-token-comment": "inline",
"code-token-built-in": "inline",
"code-token-meta": "inline",
"code-token-variable": "inline",
"code-token-operator": "inline",
"code-token-regexp": "inline",
"code-token-punctuation": "inline",
table: "table",
"table-header": "table",
"table-cell": "table",
@@ -144,6 +175,7 @@ const kinds: Record<DocxStyleSlotName, DocxStyleSlotKind> = {
"official-copy-to": "structure",
"official-printing-row": "structure",
"briefing-masthead": "structure",
"briefing-masthead-text": "structure",
"briefing-meta": "structure",
"briefing-title": "structure",
"briefing-contact": "structure",