@json-to-office/core-docx 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/ir/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA6C3D,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAuC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAEL,KAAK,MAAM,EAqCZ,MAAM,SAAS,CAAC;AAiBjB,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;IACrD,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B;;;;OAIG;IACH,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AA8FD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,UAAU,EAClB,QAAQ,GAAE,iBAAiB,EAAO,EAClC,MAAM,GAAE,cAA0B,EAClC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,iBAAiB,CAgHnB"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/ir/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAiD3D,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAuC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAEL,KAAK,MAAM,EAqCZ,MAAM,SAAS,CAAC;AAiBjB,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;IACrD,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B;;;;OAIG;IACH,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AA8FD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,UAAU,EAClB,QAAQ,GAAE,iBAAiB,EAAO,EAClC,MAAM,GAAE,cAA0B,EAClC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,iBAAiB,CAgHnB"}
@@ -8703,11 +8703,167 @@ function emptyToUndefined(formatting) {
8703
8703
  }
8704
8704
 
8705
8705
  // src/blocks/index.ts
8706
- import { KEY_TAKEAWAYS_BUDGET } from "@json-to-office/shared-docx";
8706
+ import {
8707
+ COVER_BUDGET,
8708
+ KEY_TAKEAWAYS_BUDGET,
8709
+ RUNNING_HEAD_BUDGET,
8710
+ SECTION_OPENER_BUDGET
8711
+ } from "@json-to-office/shared-docx";
8707
8712
 
8708
- // src/blocks/keyTakeaways.ts
8713
+ // src/blocks/cover.ts
8714
+ init_widthUtils();
8715
+
8716
+ // src/blocks/recipe.ts
8709
8717
  var MIN_RULE_PT = 0.25;
8710
8718
  var MAX_RULE_PT = 12;
8719
+ function clampRule(weightPt) {
8720
+ return Math.min(MAX_RULE_PT, Math.max(MIN_RULE_PT, weightPt));
8721
+ }
8722
+ var FALLBACK_EYEBROW_FONT = {
8723
+ size: 9,
8724
+ bold: true,
8725
+ color: "accent",
8726
+ case: "upper"
8727
+ };
8728
+ function hasStyle(theme, role) {
8729
+ const styles = theme.styles;
8730
+ return styles?.[role] !== void 0;
8731
+ }
8732
+ function roleProps(theme, role, fallbackFont, color) {
8733
+ if (hasStyle(theme, role)) {
8734
+ return {
8735
+ themeStyle: role,
8736
+ ...color !== void 0 && { font: { color } }
8737
+ };
8738
+ }
8739
+ return { font: { ...fallbackFont, ...color !== void 0 && { color } } };
8740
+ }
8741
+
8742
+ // src/blocks/cover.ts
8743
+ var DROP_SHARE = 0.3;
8744
+ var DROP_SHARE_WITH_LOGO = 0.22;
8745
+ var DEFAULT_LOGO_WIDTH = "25%";
8746
+ var FALLBACK = {
8747
+ ruleWeightPt: 3,
8748
+ ruleColor: "accent",
8749
+ padPt: 12,
8750
+ metaFont: { size: 9, color: "textSecondary" }
8751
+ };
8752
+ function compileCover(props, theme) {
8753
+ const recipe = theme.chrome?.cover;
8754
+ const ruleWeightPt = recipe?.rule?.weightPt ?? FALLBACK.ruleWeightPt;
8755
+ const padPt = recipe?.padPt ?? FALLBACK.padPt;
8756
+ const heightPt = getAvailableHeightTwips(theme) / 20;
8757
+ const dropPt = Math.round(
8758
+ heightPt * (props.logo ? DROP_SHARE_WITH_LOGO : DROP_SHARE)
8759
+ );
8760
+ const children = [];
8761
+ const sourceMap = {};
8762
+ const emit = (child, slots) => {
8763
+ const index = children.push(child) - 1;
8764
+ sourceMap[`/children/${index}`] = "";
8765
+ for (const [emitted, authored] of Object.entries(slots)) {
8766
+ sourceMap[`/children/${index}${emitted}`] = authored;
8767
+ }
8768
+ };
8769
+ if (props.logo) {
8770
+ const { width, height, ...source } = props.logo;
8771
+ emit(
8772
+ {
8773
+ name: "image",
8774
+ props: {
8775
+ ...source,
8776
+ width: width ?? DEFAULT_LOGO_WIDTH,
8777
+ ...height !== void 0 && { height },
8778
+ alignment: theme.chrome?.logoSlot?.alignment ?? "left",
8779
+ spacing: { before: 0, after: 0 }
8780
+ }
8781
+ },
8782
+ { "/props": "/props/logo" }
8783
+ );
8784
+ }
8785
+ const drawsRule = ruleWeightPt > 0;
8786
+ if (drawsRule) {
8787
+ emit(
8788
+ {
8789
+ name: "divider",
8790
+ props: {
8791
+ thickness: clampRule(ruleWeightPt),
8792
+ color: recipe?.rule?.color ?? FALLBACK.ruleColor,
8793
+ spacing: { before: dropPt, after: padPt }
8794
+ }
8795
+ },
8796
+ {}
8797
+ );
8798
+ }
8799
+ let dropPending = !drawsRule;
8800
+ const before = () => {
8801
+ if (!dropPending) return {};
8802
+ dropPending = false;
8803
+ return { before: dropPt };
8804
+ };
8805
+ if (props.client) {
8806
+ emit(
8807
+ {
8808
+ name: "paragraph",
8809
+ props: {
8810
+ text: props.client,
8811
+ keepNext: true,
8812
+ spacing: { ...before(), after: 4 },
8813
+ ...roleProps(theme, "eyebrow", FALLBACK_EYEBROW_FONT)
8814
+ }
8815
+ },
8816
+ { "/props/text": "/props/client" }
8817
+ );
8818
+ }
8819
+ const titleRole = recipe?.type && hasStyle(theme, recipe.type) ? recipe.type : "title";
8820
+ emit(
8821
+ {
8822
+ name: "paragraph",
8823
+ props: {
8824
+ text: props.title,
8825
+ keepNext: true,
8826
+ ...dropPending && { spacing: before() },
8827
+ ...roleProps(theme, titleRole, { size: 26, bold: true }, recipe?.color)
8828
+ }
8829
+ },
8830
+ { "/props/text": "/props/title" }
8831
+ );
8832
+ if (props.subtitle) {
8833
+ emit(
8834
+ {
8835
+ name: "paragraph",
8836
+ props: {
8837
+ text: props.subtitle,
8838
+ keepNext: true,
8839
+ ...roleProps(theme, "subtitle", { size: 13, color: "textSecondary" })
8840
+ }
8841
+ },
8842
+ { "/props/text": "/props/subtitle" }
8843
+ );
8844
+ }
8845
+ const meta = [props.date, props.confidentiality].filter(
8846
+ (part) => typeof part === "string" && part !== ""
8847
+ );
8848
+ if (meta.length > 0) {
8849
+ emit(
8850
+ {
8851
+ name: "paragraph",
8852
+ props: {
8853
+ text: meta.join(" \xB7 "),
8854
+ spacing: { before: padPt, after: 0 },
8855
+ ...roleProps(theme, "label", FALLBACK.metaFont)
8856
+ }
8857
+ },
8858
+ {
8859
+ "/props/text": props.date ? "/props/date" : "/props/confidentiality"
8860
+ }
8861
+ );
8862
+ }
8863
+ return { children, sourceMap };
8864
+ }
8865
+
8866
+ // src/blocks/keyTakeaways.ts
8711
8867
  var KEY_TAKEAWAYS_DEFAULT_LABEL = "Key takeaways";
8712
8868
  var FALLBACK_RECIPE = {
8713
8869
  ruleWeightPt: 1.5,
@@ -8718,13 +8874,9 @@ var FALLBACK_RECIPE = {
8718
8874
  function compileKeyTakeaways(props, theme) {
8719
8875
  const recipe = theme.chrome?.keyTakeaways;
8720
8876
  const labelRole = recipe?.type ?? FALLBACK_RECIPE.type;
8721
- const labelStyle = theme.styles?.[labelRole];
8722
- const ruleWeightPt = Math.min(
8723
- MAX_RULE_PT,
8724
- Math.max(
8725
- MIN_RULE_PT,
8726
- recipe?.rule?.weightPt ?? FALLBACK_RECIPE.ruleWeightPt
8727
- )
8877
+ const labelStyle = hasStyle(theme, labelRole);
8878
+ const ruleWeightPt = clampRule(
8879
+ recipe?.rule?.weightPt ?? FALLBACK_RECIPE.ruleWeightPt
8728
8880
  );
8729
8881
  const ruleColor = recipe?.rule?.color ?? recipe?.color ?? FALLBACK_RECIPE.ruleColor;
8730
8882
  const padPt = recipe?.padPt ?? FALLBACK_RECIPE.padPt;
@@ -8779,10 +8931,195 @@ function compileKeyTakeaways(props, theme) {
8779
8931
  };
8780
8932
  }
8781
8933
 
8934
+ // src/blocks/runningHead.ts
8935
+ init_styles();
8936
+ init_widthUtils();
8937
+ var FALLBACK2 = {
8938
+ headFont: { size: 8, color: "textMuted", case: "upper" },
8939
+ footFont: { size: 8, color: "textMuted" },
8940
+ ruleWeightPt: 0.5,
8941
+ ruleColor: "border"
8942
+ };
8943
+ var PAGE_OF_TOTAL = "{PAGE} / {TOTAL_PAGES}";
8944
+ function sectionMeasureTwips(theme, page) {
8945
+ if (!page || page.size === void 0 && page.margins === void 0) {
8946
+ return getAvailableWidthTwips(theme);
8947
+ }
8948
+ const width = page.size ? getPageDimensions(page.size).width : getPageDimensions(theme.page.size).width;
8949
+ const margins = getDocumentMargins(theme);
8950
+ const left = page.margins?.left ?? margins.left ?? 1440;
8951
+ const right = page.margins?.right ?? margins.right ?? 1440;
8952
+ return Math.max(0, width - left - right);
8953
+ }
8954
+ function compileRunningHead(props, theme, scope) {
8955
+ const measure = sectionMeasureTwips(theme, scope.page);
8956
+ const head = theme.chrome?.runningHead;
8957
+ const foot = theme.chrome?.confidentialFooter;
8958
+ const slot = (name) => props[name] !== void 0 ? `${scope.block}/props/${name}` : void 0;
8959
+ const header = [];
8960
+ const headerMap = {};
8961
+ const title = props.title ?? scope.documentTitle ?? "";
8962
+ const tracker = scope.opener?.text ?? props.tracker ?? "";
8963
+ if (title !== "" || tracker !== "") {
8964
+ const both = title !== "" && tracker !== "";
8965
+ header.push({
8966
+ name: "paragraph",
8967
+ props: {
8968
+ text: both ? `${title} ${tracker}` : title || tracker,
8969
+ ...both ? { tabStops: [{ type: "right", position: measure }] } : { alignment: title !== "" ? "left" : head?.alignment ?? "right" },
8970
+ spacing: { before: 0, after: 0 },
8971
+ ...roleProps(
8972
+ theme,
8973
+ head?.type ?? "tracker",
8974
+ FALLBACK2.headFont,
8975
+ head?.color
8976
+ )
8977
+ }
8978
+ });
8979
+ headerMap["/0"] = scope.block;
8980
+ headerMap["/0/props/text"] = (tracker !== "" ? scope.opener?.slot ?? slot("tracker") : slot("title")) ?? scope.block;
8981
+ }
8982
+ const headRule = head?.rule?.weightPt ?? FALLBACK2.ruleWeightPt;
8983
+ if (headRule > 0) {
8984
+ header.push({
8985
+ name: "divider",
8986
+ props: {
8987
+ thickness: clampRule(headRule),
8988
+ color: head?.rule?.color ?? FALLBACK2.ruleColor,
8989
+ spacing: { before: 2, after: 0 }
8990
+ }
8991
+ });
8992
+ headerMap[`/${header.length - 1}`] = scope.block;
8993
+ }
8994
+ const footer = [];
8995
+ const footerMap = {};
8996
+ const footRule = foot?.rule?.weightPt ?? FALLBACK2.ruleWeightPt;
8997
+ if (footRule > 0) {
8998
+ footer.push({
8999
+ name: "divider",
9000
+ props: {
9001
+ thickness: clampRule(footRule),
9002
+ color: foot?.rule?.color ?? FALLBACK2.ruleColor,
9003
+ spacing: { before: 0, after: 4 }
9004
+ }
9005
+ });
9006
+ footerMap["/0"] = scope.block;
9007
+ }
9008
+ const parts = [
9009
+ props.confidentiality ?? "",
9010
+ props.pageNumbers === false ? "" : PAGE_OF_TOTAL,
9011
+ props.date ?? ""
9012
+ ];
9013
+ const present = parts.filter((part) => part !== "");
9014
+ if (present.length > 0) {
9015
+ const single = present.length === 1;
9016
+ const alignment2 = present[0] === PAGE_OF_TOTAL ? "center" : foot?.alignment ?? "left";
9017
+ footer.push({
9018
+ name: "paragraph",
9019
+ props: {
9020
+ text: single ? present[0] : parts.join(" ").replace(/\t+$/, ""),
9021
+ ...single ? { alignment: alignment2 } : {
9022
+ tabStops: [
9023
+ { type: "center", position: Math.round(measure / 2) },
9024
+ { type: "right", position: measure }
9025
+ ]
9026
+ },
9027
+ spacing: { before: 0, after: 0 },
9028
+ ...roleProps(
9029
+ theme,
9030
+ foot?.type ?? "footer",
9031
+ FALLBACK2.footFont,
9032
+ foot?.color
9033
+ )
9034
+ }
9035
+ });
9036
+ const index = footer.length - 1;
9037
+ footerMap[`/${index}`] = scope.block;
9038
+ footerMap[`/${index}/props/text`] = slot("confidentiality") ?? slot("date") ?? scope.block;
9039
+ }
9040
+ return {
9041
+ header: { children: header, sourceMap: headerMap },
9042
+ footer: { children: footer, sourceMap: footerMap }
9043
+ };
9044
+ }
9045
+
9046
+ // src/blocks/sectionOpener.ts
9047
+ function compileSectionOpener(props, theme) {
9048
+ const children = [];
9049
+ const sourceMap = {};
9050
+ const pageBreak = props.pageBreak === true;
9051
+ if (props.number !== void 0) {
9052
+ children.push({
9053
+ name: "paragraph",
9054
+ props: {
9055
+ text: String(props.number),
9056
+ keepNext: true,
9057
+ spacing: { after: 2 },
9058
+ ...pageBreak && { pageBreak: true },
9059
+ ...roleProps(theme, "eyebrow", FALLBACK_EYEBROW_FONT)
9060
+ }
9061
+ });
9062
+ sourceMap["/children/0"] = "";
9063
+ sourceMap["/children/0/props/text"] = "/props/number";
9064
+ }
9065
+ const index = children.length;
9066
+ children.push({
9067
+ name: "heading",
9068
+ props: {
9069
+ text: props.title,
9070
+ level: 1,
9071
+ ...pageBreak && index === 0 && { pageBreak: true }
9072
+ }
9073
+ });
9074
+ sourceMap[`/children/${index}`] = "";
9075
+ sourceMap[`/children/${index}/props/text`] = "/props/title";
9076
+ return { children, sourceMap };
9077
+ }
9078
+ function sectionTracker(section2) {
9079
+ if (!Array.isArray(section2.children)) return void 0;
9080
+ for (const [index, child] of section2.children.entries()) {
9081
+ if (typeof child !== "object" || child === null || child.name !== "section-opener" || child.enabled === false) {
9082
+ continue;
9083
+ }
9084
+ const props = child.props;
9085
+ if (!props) continue;
9086
+ if (typeof props.tracker === "string" && props.tracker !== "") {
9087
+ return { text: props.tracker, slot: `/children/${index}/props/tracker` };
9088
+ }
9089
+ if (typeof props.title === "string" && props.title !== "") {
9090
+ return { text: props.title, slot: `/children/${index}/props/title` };
9091
+ }
9092
+ }
9093
+ return void 0;
9094
+ }
9095
+
8782
9096
  // src/blocks/index.ts
8783
- var BLOCK_NAMES = ["key-takeaways"];
9097
+ var BLOCK_NAMES = [
9098
+ "key-takeaways",
9099
+ "cover",
9100
+ "section-opener",
9101
+ "running-head"
9102
+ ];
9103
+ function slotBudget(block2, props, pointer, slot, maxWords) {
9104
+ const value = props[slot];
9105
+ return typeof value === "string" ? [
9106
+ {
9107
+ block: block2,
9108
+ slot,
9109
+ path: `${pointer}/props/${slot}`,
9110
+ words: wordCount(value),
9111
+ maxWords
9112
+ }
9113
+ ] : [];
9114
+ }
9115
+ function budgetsOf(block2, budget) {
9116
+ return (props, pointer) => Object.entries(budget).flatMap(
9117
+ ([slot, { maxWords }]) => slotBudget(block2, props, pointer, slot, maxWords)
9118
+ );
9119
+ }
8784
9120
  var BLOCKS = {
8785
9121
  "key-takeaways": {
9122
+ kind: "flow",
8786
9123
  compile: (props, theme) => compileKeyTakeaways(props, theme),
8787
9124
  budgets: (props, pointer) => (Array.isArray(props.items) ? props.items : []).flatMap(
8788
9125
  (item, index) => typeof item === "string" ? [
@@ -8795,25 +9132,95 @@ var BLOCKS = {
8795
9132
  }
8796
9133
  ] : []
8797
9134
  )
9135
+ },
9136
+ cover: {
9137
+ kind: "flow",
9138
+ compile: (props, theme) => compileCover(props, theme),
9139
+ budgets: budgetsOf("cover", COVER_BUDGET)
9140
+ },
9141
+ "section-opener": {
9142
+ kind: "flow",
9143
+ compile: (props, theme) => compileSectionOpener(props, theme),
9144
+ budgets: budgetsOf("section-opener", SECTION_OPENER_BUDGET)
9145
+ },
9146
+ "running-head": {
9147
+ kind: "chrome",
9148
+ budgets: budgetsOf("running-head", RUNNING_HEAD_BUDGET)
8798
9149
  }
8799
9150
  };
8800
9151
  function isBlockName(name) {
8801
9152
  return BLOCK_NAMES.includes(name);
8802
9153
  }
9154
+ function isChromeBlockName(name) {
9155
+ return isBlockName(name) && BLOCKS[name].kind === "chrome";
9156
+ }
8803
9157
  function isRecord(value) {
8804
9158
  return typeof value === "object" && value !== null && !Array.isArray(value);
8805
9159
  }
9160
+ var SECTION_CHILD = /^\/children\/\d+\/children\/\d+$/;
9161
+ var SECTION = /^\/children\/(\d+)$/;
9162
+ function planChrome(document, theme) {
9163
+ const plans = /* @__PURE__ */ new Map();
9164
+ if (!isRecord(document) || document.name !== "docx" || !Array.isArray(document.children)) {
9165
+ return plans;
9166
+ }
9167
+ const metadata = isRecord(document.props) ? document.props.metadata : void 0;
9168
+ const documentTitle = isRecord(metadata) && typeof metadata.title === "string" ? metadata.title : void 0;
9169
+ let inForce;
9170
+ document.children.forEach((section2, index) => {
9171
+ if (!isRecord(section2) || section2.name !== "section" || section2.enabled === false) {
9172
+ return;
9173
+ }
9174
+ const sectionPointer = `/children/${index}`;
9175
+ const children = Array.isArray(section2.children) ? section2.children : [];
9176
+ children.forEach((child, childIndex) => {
9177
+ if (isRecord(child) && isChromeBlockName(child.name) && child.enabled !== false) {
9178
+ inForce = {
9179
+ block: `${sectionPointer}/children/${childIndex}`,
9180
+ props: isRecord(child.props) ? child.props : {}
9181
+ };
9182
+ }
9183
+ });
9184
+ if (!inForce) return;
9185
+ const opener = sectionTracker(section2);
9186
+ const sectionProps = isRecord(section2.props) ? section2.props : {};
9187
+ plans.set(
9188
+ index,
9189
+ compileRunningHead(inForce.props, theme, {
9190
+ block: inForce.block,
9191
+ documentTitle,
9192
+ ...opener && {
9193
+ opener: {
9194
+ text: opener.text,
9195
+ slot: `${sectionPointer}${opener.slot}`
9196
+ }
9197
+ },
9198
+ ...isRecord(sectionProps.page) && {
9199
+ page: sectionProps.page
9200
+ }
9201
+ })
9202
+ );
9203
+ });
9204
+ return plans;
9205
+ }
8806
9206
  function expandBlocks(document, theme) {
8807
9207
  const sourceMap = {};
8808
9208
  const blocks = [];
9209
+ const chrome = planChrome(document, theme);
8809
9210
  const walk2 = (node, pointer) => {
8810
9211
  if (Array.isArray(node)) {
8811
9212
  return node.map((child, index) => walk2(child, `${pointer}/${index}`));
8812
9213
  }
8813
9214
  if (!isRecord(node)) return node;
8814
- if (typeof node.name === "string" && isRecord(node.props)) {
8815
- if (node.enabled !== false && isBlockName(node.name)) {
8816
- const compiled = BLOCKS[node.name].compile(node.props, theme);
9215
+ if (typeof node.name === "string" && node.enabled !== false && isBlockName(node.name)) {
9216
+ const definition = BLOCKS[node.name];
9217
+ if (definition.kind === "chrome") {
9218
+ if (!SECTION_CHILD.test(pointer)) return node;
9219
+ blocks.push(pointer);
9220
+ return { ...node, children: [] };
9221
+ }
9222
+ if (isRecord(node.props)) {
9223
+ const compiled = definition.compile(node.props, theme);
8817
9224
  blocks.push(pointer);
8818
9225
  for (const [emitted, authored] of Object.entries(compiled.sourceMap)) {
8819
9226
  sourceMap[`${pointer}${emitted}`] = `${pointer}${authored}`;
@@ -8862,6 +9269,30 @@ function expandBlocks(document, theme) {
8862
9269
  if (Array.isArray(node.children)) {
8863
9270
  next.children = walk2(node.children, `${pointer}/children`);
8864
9271
  }
9272
+ const section2 = SECTION.exec(pointer);
9273
+ if (section2 && node.name === "section" && node.enabled !== false) {
9274
+ const plan = chrome.get(Number(section2[1]));
9275
+ if (plan) {
9276
+ const props = isRecord(next.props) ? { ...next.props } : {};
9277
+ let changed = false;
9278
+ for (const part of ["header", "footer"]) {
9279
+ const { children, sourceMap: partMap } = plan[part];
9280
+ if (props[part] !== void 0 || children.length === 0) continue;
9281
+ props[part] = children;
9282
+ changed = true;
9283
+ const base = `${pointer}/props/${part}`;
9284
+ sourceMap[base] = partMap["/0"] ?? pointer;
9285
+ for (const [emitted, authored] of Object.entries(partMap)) {
9286
+ sourceMap[`${base}${emitted}`] = authored;
9287
+ }
9288
+ }
9289
+ if (props.pageBreak === void 0) {
9290
+ props.pageBreak = true;
9291
+ changed = true;
9292
+ }
9293
+ if (changed) next.props = props;
9294
+ }
9295
+ }
8865
9296
  return next;
8866
9297
  };
8867
9298
  const expanded = walk2(document, "");
@@ -9203,33 +9634,47 @@ function compileChromeParagraph(component, scope) {
9203
9634
  const text = String(props.text ?? "");
9204
9635
  if (reportUnlowered(component, props, text, scope)) return [];
9205
9636
  const font = props.font ?? {};
9206
- const normal = getNormalStyle(ctx.theme);
9637
+ const named = chromeStyle(ctx.theme, props.themeStyle);
9638
+ const base = { ...getNormalStyle(ctx.theme), ...named };
9639
+ const styleId = named ? paragraphStyleId(props.themeStyle) ?? "Normal" : "Normal";
9640
+ if (styleId !== "Normal" && !ctx.styleIds.has(styleId)) {
9641
+ ctx.styleIds.add(styleId);
9642
+ }
9207
9643
  const chromeProps = {
9208
9644
  text,
9209
- ...props.alignment ? { alignment: props.alignment } : {},
9645
+ ...named ? { themeStyle: props.themeStyle } : {},
9646
+ ...props.alignment ? { alignment: props.alignment } : named?.alignment ? { alignment: named.alignment } : {},
9210
9647
  ...props.spacing ? { spacing: props.spacing } : {},
9648
+ ...props.indent ? { indent: props.indent } : {},
9649
+ ...props.tabStops ? { tabStops: props.tabStops } : {},
9211
9650
  ...props.boldColor ? { boldColor: props.boldColor } : {},
9212
9651
  font: {
9213
- family: font.family || resolveFontFamily(ctx.theme, normal.font) || getThemeFonts(ctx.theme).body.family,
9214
- size: font.size ?? normal.size ?? 11,
9215
- color: font.color || normal.color || "textPrimary",
9216
- bold: font.bold ?? false,
9217
- italic: font.italic ?? false,
9652
+ family: font.family || resolveFontFamily(ctx.theme, base.font) || getThemeFonts(ctx.theme).body.family,
9653
+ size: font.size ?? base.size ?? 11,
9654
+ color: font.color || base.color || "textPrimary",
9655
+ bold: font.bold ?? base.bold ?? false,
9656
+ italic: font.italic ?? base.italic ?? false,
9218
9657
  ...font.underline !== void 0 ? { underline: font.underline } : {},
9219
- ...font.fontWeight !== void 0 ? { fontWeight: font.fontWeight } : {},
9220
- ...font.case !== void 0 ? { case: font.case } : {},
9658
+ ...(font.fontWeight ?? base.fontWeight) !== void 0 ? { fontWeight: font.fontWeight ?? base.fontWeight } : {},
9659
+ ...(font.case ?? base.case) !== void 0 ? { case: font.case ?? base.case } : {},
9660
+ ...(font.characterSpacing ?? base.characterSpacing) !== void 0 ? { characterSpacing: font.characterSpacing ?? base.characterSpacing } : {},
9221
9661
  ...font.lineSpacing !== void 0 ? { lineSpacing: font.lineSpacing } : {}
9222
9662
  }
9223
9663
  };
9224
9664
  return [
9225
9665
  paragraphNode(scope, compileRuns(chromeProps, text, ctx, scope.path), {
9226
- styleId: "Normal",
9666
+ styleId,
9227
9667
  formatting: paragraphFormatting(chromeProps, ctx, {
9228
9668
  alwaysSpacing: true
9229
9669
  })
9230
9670
  })
9231
9671
  ];
9232
9672
  }
9673
+ function chromeStyle(theme, themeStyle) {
9674
+ if (typeof themeStyle !== "string" || !themeStyle) return void 0;
9675
+ const styles = getThemeStyles(theme);
9676
+ return styles[themeStyle] ?? styles[themeStyle.toLowerCase()];
9677
+ }
9233
9678
  function compileChromeImage(component, scope) {
9234
9679
  const { ctx, path: path3 } = scope;
9235
9680
  const props = component.props ?? {};
@@ -9329,8 +9774,8 @@ function compileComponent(component, scope) {
9329
9774
  }
9330
9775
  }
9331
9776
  function compileBlock(component, scope) {
9332
- const children = component.children ?? [];
9333
- if (children.length === 0) {
9777
+ const children = component.children;
9778
+ if (!Array.isArray(children)) {
9334
9779
  scope.ctx.unsupported.push({ name: component.name, path: scope.path });
9335
9780
  return [];
9336
9781
  }