@json-to-office/core-docx 2.4.0 → 2.6.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.
Files changed (50) hide show
  1. package/dist/blocks/index.d.ts +58 -0
  2. package/dist/blocks/index.d.ts.map +1 -0
  3. package/dist/blocks/keyTakeaways.d.ts +20 -0
  4. package/dist/blocks/keyTakeaways.d.ts.map +1 -0
  5. package/dist/blocks/types.d.ts +15 -0
  6. package/dist/blocks/types.d.ts.map +1 -0
  7. package/dist/components/highcharts.d.ts +2 -2
  8. package/dist/components/highcharts.d.ts.map +1 -1
  9. package/dist/core/componentTransform.d.ts +8 -0
  10. package/dist/core/componentTransform.d.ts.map +1 -1
  11. package/dist/core/desugarExternals.d.ts +2 -0
  12. package/dist/core/desugarExternals.d.ts.map +1 -1
  13. package/dist/core/generateFromIr.d.ts.map +1 -1
  14. package/dist/core/generationContext.d.ts.map +1 -1
  15. package/dist/index.d.ts +3 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1027 -170
  18. package/dist/index.js.map +1 -1
  19. package/dist/ir/compiler.d.ts.map +1 -1
  20. package/dist/plugin/example/index.js +1010 -182
  21. package/dist/plugin/example/index.js.map +1 -1
  22. package/dist/quality/facts.d.ts +23 -1
  23. package/dist/quality/facts.d.ts.map +1 -1
  24. package/dist/quality/rules.d.ts +6 -0
  25. package/dist/quality/rules.d.ts.map +1 -1
  26. package/dist/renderers/docxjs/emit.d.ts.map +1 -1
  27. package/dist/renderers/docxjs/index.d.ts.map +1 -1
  28. package/dist/renderers/office-open/index.d.ts.map +1 -1
  29. package/dist/styles/index.d.ts +1 -1
  30. package/dist/styles/index.d.ts.map +1 -1
  31. package/dist/styles/themeToStyles.d.ts.map +1 -1
  32. package/dist/styles/utils/colorUtils.d.ts +12 -1
  33. package/dist/styles/utils/colorUtils.d.ts.map +1 -1
  34. package/dist/styles/utils/styleHelpers.d.ts +2 -0
  35. package/dist/styles/utils/styleHelpers.d.ts.map +1 -1
  36. package/dist/templates/themes/consulting.docx.theme.json +307 -0
  37. package/dist/templates/themes/index.d.ts +3649 -666
  38. package/dist/templates/themes/index.d.ts.map +1 -1
  39. package/dist/themes/defaults.d.ts +183 -180
  40. package/dist/themes/defaults.d.ts.map +1 -1
  41. package/dist/themes/design-system.d.ts +4 -0
  42. package/dist/themes/design-system.d.ts.map +1 -0
  43. package/dist/themes/overrides.d.ts +2 -1
  44. package/dist/themes/overrides.d.ts.map +1 -1
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/dist/utils/normalizeTextCase.d.ts +15 -0
  47. package/dist/utils/normalizeTextCase.d.ts.map +1 -0
  48. package/dist/utils/packageDocument.d.ts +2 -0
  49. package/dist/utils/packageDocument.d.ts.map +1 -1
  50. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ var __export = (target, all) => {
15
15
  };
16
16
 
17
17
  // src/themes/defaults.ts
18
+ import { designColors } from "@json-to-office/shared";
18
19
  function ensureThemeDefaults(theme) {
19
20
  return {
20
21
  ...theme,
@@ -47,10 +48,13 @@ function ensureThemeDefaults(theme) {
47
48
  };
48
49
  }
49
50
  function getThemeColors(theme) {
50
- return {
51
- ...DEFAULT_COLORS,
52
- ...theme.colors || {}
53
- };
51
+ return designColors(
52
+ {
53
+ ...DEFAULT_COLORS,
54
+ ...theme.colors || {}
55
+ },
56
+ theme.palette
57
+ );
54
58
  }
55
59
  function getThemeFonts(theme) {
56
60
  return {
@@ -188,6 +192,10 @@ var init_defaults = __esm({
188
192
  });
189
193
 
190
194
  // src/styles/utils/colorUtils.ts
195
+ import {
196
+ DEFAULT_CHART_THEME_COLORS,
197
+ resolveDesignColor
198
+ } from "@json-to-office/shared";
191
199
  function resolveColor(colorValue, theme) {
192
200
  if (colorValue.startsWith("#")) {
193
201
  const hexValue = colorValue.slice(1);
@@ -201,7 +209,11 @@ function resolveColor(colorValue, theme) {
201
209
  const colors = getThemeColors(theme);
202
210
  const resolvedColor = colors[colorValue];
203
211
  if (typeof resolvedColor === "string") {
204
- return resolveColor(resolvedColor, theme);
212
+ const hex = resolveDesignColor(colorValue, colors);
213
+ if (hex) return hex.toUpperCase();
214
+ throw new Error(
215
+ `Unresolvable theme color: "${colorValue}" (missing reference or cycle).`
216
+ );
205
217
  }
206
218
  if (/^[0-9A-Fa-f]{6}$/.test(colorValue)) {
207
219
  return colorValue.toUpperCase();
@@ -210,6 +222,15 @@ function resolveColor(colorValue, theme) {
210
222
  `Invalid color value: "${colorValue}". Must be a hex color with # prefix (e.g., "#000000") or a valid theme color name.`
211
223
  );
212
224
  }
225
+ function chartPaletteValues(theme) {
226
+ const explicit = theme.palette?.chart;
227
+ if (explicit) return [...explicit];
228
+ const colors = getThemeColors(theme);
229
+ return DEFAULT_CHART_THEME_COLORS.flatMap((token) => {
230
+ const value = colors[token];
231
+ return typeof value === "string" && value.length > 0 ? [value] : [];
232
+ });
233
+ }
213
234
  var init_colorUtils = __esm({
214
235
  "src/styles/utils/colorUtils.ts"() {
215
236
  "use strict";
@@ -217,43 +238,6 @@ var init_colorUtils = __esm({
217
238
  }
218
239
  });
219
240
 
220
- // src/utils/generationContext.ts
221
- import { AsyncLocalStorage } from "async_hooks";
222
- import { isAbsolute, resolve } from "path";
223
- function runWithBaseDir(baseDir, callback) {
224
- return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
225
- }
226
- function resolveFromBaseDir(filePath) {
227
- const base = baseDirStorage.getStore();
228
- if (!base || isAbsolute(filePath)) return filePath;
229
- return resolve(base, filePath);
230
- }
231
- function runWithWarnings(warnings, callback) {
232
- return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
233
- }
234
- function reportWarning(component, code, message, context) {
235
- const warnings = warningsStorage.getStore();
236
- if (warnings) {
237
- warnings.push({
238
- component,
239
- message,
240
- severity: "warning",
241
- context: { code, ...context }
242
- });
243
- return;
244
- }
245
- console.warn(`[json-to-docx] [${code}] ${message}`);
246
- }
247
- var generationDateStorage, baseDirStorage, warningsStorage;
248
- var init_generationContext = __esm({
249
- "src/utils/generationContext.ts"() {
250
- "use strict";
251
- generationDateStorage = new AsyncLocalStorage();
252
- baseDirStorage = new AsyncLocalStorage();
253
- warningsStorage = new AsyncLocalStorage();
254
- }
255
- });
256
-
257
241
  // src/templates/themes/minimal.docx.theme.json
258
242
  var minimal_docx_theme_default;
259
243
  var init_minimal_docx_theme = __esm({
@@ -859,6 +843,320 @@ var init_vermilion_docx_theme = __esm({
859
843
  }
860
844
  });
861
845
 
846
+ // src/templates/themes/consulting.docx.theme.json
847
+ var consulting_docx_theme_default;
848
+ var init_consulting_docx_theme = __esm({
849
+ "src/templates/themes/consulting.docx.theme.json"() {
850
+ consulting_docx_theme_default = {
851
+ name: "consulting",
852
+ displayName: "Consulting House",
853
+ description: "The house style for client and technical reports \u2014 near-black ink, three greys and one deep-blue accent, Calibri body under Arial headings, hairline rules, no fills behind body text, safe fonts only",
854
+ version: "1.0.0",
855
+ colors: {
856
+ primary: "#1A1F26",
857
+ secondary: "#4B5563",
858
+ accent: "#1B4F8A",
859
+ text: "#1A1F26",
860
+ background: "#FFFFFF",
861
+ border: "#C9CED6",
862
+ textPrimary: "#1A1F26",
863
+ textSecondary: "#4B5563",
864
+ textMuted: "#7B8794",
865
+ borderPrimary: "#C9CED6",
866
+ borderSecondary: "#E4E7EB",
867
+ backgroundPrimary: "#FFFFFF",
868
+ backgroundSecondary: "#F2F4F7",
869
+ accent4: "#5B8DC9",
870
+ accent5: "#A9C4E4",
871
+ accent6: "#7B8794"
872
+ },
873
+ palette: {
874
+ rule: "#C9CED6",
875
+ textMuted: "#7B8794",
876
+ onPrimary: "#FFFFFF",
877
+ surfaceInverse: "#1A1F26",
878
+ positive: "#1E7F4F",
879
+ negative: "#B42318",
880
+ chart: ["accent", "secondary", "accent4", "textMuted", "accent5", "rule"]
881
+ },
882
+ fonts: {
883
+ heading: { family: "Arial", size: 16 },
884
+ body: { family: "Calibri", size: 10.5 },
885
+ mono: { family: "Consolas", size: 9.5 },
886
+ light: { family: "Arial", size: 16 }
887
+ },
888
+ page: {
889
+ size: "A4",
890
+ margins: {
891
+ top: 1440,
892
+ bottom: 1440,
893
+ left: 1440,
894
+ right: 1440,
895
+ header: 680,
896
+ footer: 680,
897
+ gutter: 0
898
+ }
899
+ },
900
+ typography: {
901
+ roles: {
902
+ eyebrow: {
903
+ face: "heading",
904
+ size: 8,
905
+ weight: 700,
906
+ case: "upper",
907
+ tracking: 8,
908
+ color: "accent",
909
+ spaceAfter: 4
910
+ },
911
+ display: {
912
+ face: "heading",
913
+ weight: 700,
914
+ color: "primary",
915
+ lineHeight: 1.05,
916
+ spaceAfter: 8
917
+ },
918
+ stat: {
919
+ face: "heading",
920
+ weight: 700,
921
+ color: "accent",
922
+ lineHeight: 1
923
+ },
924
+ quote: {
925
+ face: "body",
926
+ color: "textSecondary",
927
+ lineHeight: 1.3
928
+ },
929
+ label: {
930
+ face: "body",
931
+ size: 9,
932
+ weight: 700,
933
+ color: "textSecondary"
934
+ },
935
+ footer: { face: "body", size: 8, color: "textMuted" },
936
+ tableHeader: {
937
+ face: "body",
938
+ size: 9,
939
+ weight: 700,
940
+ color: "primary"
941
+ },
942
+ tableCell: { face: "body", size: 9.5, color: "textPrimary" },
943
+ chartLabel: {
944
+ face: "body",
945
+ size: 9,
946
+ weight: 400,
947
+ color: "textSecondary"
948
+ },
949
+ tracker: {
950
+ face: "body",
951
+ size: 8,
952
+ case: "upper",
953
+ tracking: 6,
954
+ color: "textMuted"
955
+ },
956
+ source: { face: "body", size: 8, color: "textMuted" }
957
+ },
958
+ scale: {
959
+ a4: { base: 10.5, ratio: 1.2, baselinePt: 2 },
960
+ letter: { base: 10.5, ratio: 1.2, baselinePt: 2 }
961
+ }
962
+ },
963
+ spacing: {
964
+ basePt: 8,
965
+ blockGap: { tight: 4, normal: 8, loose: 16 },
966
+ canvas: {
967
+ a4: { safeAreaIn: 1, gutterIn: 0, columns: 12 },
968
+ letter: { safeAreaIn: 1, gutterIn: 0, columns: 12 }
969
+ }
970
+ },
971
+ chrome: {
972
+ runningHead: {
973
+ type: "tracker",
974
+ color: "textMuted",
975
+ rule: { weightPt: 0.5, color: "rule" },
976
+ alignment: "right"
977
+ },
978
+ tracker: {
979
+ type: "tracker",
980
+ color: "textMuted",
981
+ alignment: "right"
982
+ },
983
+ actionTitle: { type: "display", color: "primary" },
984
+ keyTakeaways: {
985
+ type: "label",
986
+ color: "primary",
987
+ rule: { weightPt: 2, color: "accent" },
988
+ padPt: 8
989
+ },
990
+ sourceLine: { type: "source", color: "textMuted" },
991
+ confidentialFooter: {
992
+ type: "footer",
993
+ color: "textMuted",
994
+ rule: { weightPt: 0.5, color: "rule" },
995
+ alignment: "left"
996
+ },
997
+ logoSlot: { alignment: "right" },
998
+ cover: {
999
+ type: "display",
1000
+ color: "primary",
1001
+ rule: { weightPt: 3, color: "accent" }
1002
+ }
1003
+ },
1004
+ motif: {
1005
+ kind: "rule",
1006
+ color: "accent",
1007
+ weightPt: 3,
1008
+ placement: "top"
1009
+ },
1010
+ styles: {
1011
+ normal: {
1012
+ font: "body",
1013
+ color: "textPrimary",
1014
+ lineSpacing: { type: "multiple", value: 1.2 },
1015
+ alignment: "left",
1016
+ spacing: { after: 8 }
1017
+ },
1018
+ title: {
1019
+ font: "heading",
1020
+ size: 26,
1021
+ bold: true,
1022
+ color: "primary",
1023
+ alignment: "left",
1024
+ lineSpacing: { type: "multiple", value: 1.05 },
1025
+ spacing: { before: 0, after: 6 }
1026
+ },
1027
+ subtitle: {
1028
+ font: "body",
1029
+ size: 13,
1030
+ color: "textSecondary",
1031
+ alignment: "left",
1032
+ lineSpacing: { type: "multiple", value: 1.2 },
1033
+ spacing: { before: 0, after: 18 }
1034
+ },
1035
+ heading1: {
1036
+ font: "heading",
1037
+ size: 16,
1038
+ bold: true,
1039
+ color: "primary",
1040
+ lineSpacing: { type: "multiple", value: 1.1 },
1041
+ spacing: { before: 24, after: 8 },
1042
+ keepNext: true,
1043
+ alignment: "left",
1044
+ borders: {
1045
+ bottom: { style: "single", size: 4, color: "rule", space: 4 }
1046
+ }
1047
+ },
1048
+ heading2: {
1049
+ font: "heading",
1050
+ size: 12.5,
1051
+ bold: true,
1052
+ color: "primary",
1053
+ spacing: { before: 16, after: 6 },
1054
+ keepNext: true,
1055
+ alignment: "left"
1056
+ },
1057
+ heading3: {
1058
+ font: "body",
1059
+ size: 11,
1060
+ bold: true,
1061
+ color: "primary",
1062
+ spacing: { before: 12, after: 4 },
1063
+ keepNext: true,
1064
+ alignment: "left"
1065
+ },
1066
+ heading4: {
1067
+ font: "body",
1068
+ size: 10.5,
1069
+ bold: true,
1070
+ color: "textSecondary",
1071
+ spacing: { before: 10, after: 4 },
1072
+ keepNext: true,
1073
+ alignment: "left"
1074
+ },
1075
+ heading5: {
1076
+ font: "body",
1077
+ size: 10.5,
1078
+ bold: false,
1079
+ italic: true,
1080
+ color: "textSecondary",
1081
+ spacing: { before: 8, after: 4 },
1082
+ keepNext: true,
1083
+ alignment: "left"
1084
+ },
1085
+ heading6: {
1086
+ font: "body",
1087
+ size: 9,
1088
+ bold: true,
1089
+ case: "upper",
1090
+ color: "textMuted",
1091
+ characterSpacing: { type: "expanded", value: 20 },
1092
+ spacing: { before: 8, after: 4 },
1093
+ keepNext: true,
1094
+ alignment: "left"
1095
+ },
1096
+ TOC1: {
1097
+ font: "body",
1098
+ size: 10.5,
1099
+ bold: true,
1100
+ color: "textPrimary",
1101
+ spacing: { before: 4, after: 3 },
1102
+ tabStops: [{ type: "right", position: 9026, leader: "none" }]
1103
+ },
1104
+ TOC2: {
1105
+ font: "body",
1106
+ size: 10.5,
1107
+ color: "textSecondary",
1108
+ indent: { left: 240 },
1109
+ spacing: { after: 2 },
1110
+ tabStops: [{ type: "right", position: 9026, leader: "none" }]
1111
+ },
1112
+ TOC3: {
1113
+ font: "body",
1114
+ size: 10,
1115
+ color: "textMuted",
1116
+ indent: { left: 480 },
1117
+ spacing: { after: 2 },
1118
+ tabStops: [{ type: "right", position: 9026, leader: "none" }]
1119
+ }
1120
+ },
1121
+ componentDefaults: {
1122
+ table: {
1123
+ width: 100,
1124
+ borderColor: "#C9CED6",
1125
+ borderSize: 0.5,
1126
+ hideBorders: { left: true, right: true, insideVertical: true },
1127
+ cellDefaults: {
1128
+ font: { size: 9.5 },
1129
+ padding: { top: 4, bottom: 4 },
1130
+ verticalAlignment: "middle"
1131
+ },
1132
+ headerCellDefaults: {
1133
+ color: "#1A1F26",
1134
+ font: { size: 9, bold: true },
1135
+ padding: { top: 0, bottom: 5 },
1136
+ verticalAlignment: "bottom",
1137
+ borderColor: { bottom: "#1A1F26" },
1138
+ borderSize: { bottom: 0.75 }
1139
+ }
1140
+ },
1141
+ list: {
1142
+ format: "bullet",
1143
+ bullet: "\u2022",
1144
+ spacing: { item: 2 }
1145
+ },
1146
+ image: {
1147
+ alignment: "center"
1148
+ },
1149
+ statistic: {
1150
+ alignment: "left"
1151
+ },
1152
+ section: {
1153
+ pageBreak: false
1154
+ }
1155
+ }
1156
+ };
1157
+ }
1158
+ });
1159
+
862
1160
  // src/templates/themes/index.ts
863
1161
  import fs from "fs";
864
1162
  import path from "path";
@@ -873,7 +1171,8 @@ function loadThemesFromJson() {
873
1171
  const themes2 = {
874
1172
  minimal: ensureThemeDefaults(minimal_docx_theme_default),
875
1173
  devportal: ensureThemeDefaults(devportal_docx_theme_default),
876
- vermilion: ensureThemeDefaults(vermilion_docx_theme_default)
1174
+ vermilion: ensureThemeDefaults(vermilion_docx_theme_default),
1175
+ consulting: ensureThemeDefaults(consulting_docx_theme_default)
877
1176
  };
878
1177
  try {
879
1178
  const __filename = fileURLToPath(import.meta.url);
@@ -911,7 +1210,7 @@ function loadThemesFromJson() {
911
1210
  _themesCache = themes2;
912
1211
  return themes2;
913
1212
  }
914
- var _themesCache, themes, getTheme, getThemeWithFallback, hasTheme, getThemeNames, minimalTheme, devportalTheme, vermilionTheme;
1213
+ var _themesCache, themes, getTheme, getThemeWithFallback, hasTheme, getThemeNames, minimalTheme, devportalTheme, vermilionTheme, consultingTheme;
915
1214
  var init_themes = __esm({
916
1215
  "src/templates/themes/index.ts"() {
917
1216
  "use strict";
@@ -919,6 +1218,7 @@ var init_themes = __esm({
919
1218
  init_minimal_docx_theme();
920
1219
  init_devportal_docx_theme();
921
1220
  init_vermilion_docx_theme();
1221
+ init_consulting_docx_theme();
922
1222
  _themesCache = null;
923
1223
  themes = loadThemesFromJson();
924
1224
  getTheme = (themeName) => {
@@ -942,6 +1242,7 @@ var init_themes = __esm({
942
1242
  minimalTheme = themes["minimal"];
943
1243
  devportalTheme = themes["devportal"];
944
1244
  vermilionTheme = themes["vermilion"];
1245
+ consultingTheme = themes["consulting"];
945
1246
  }
946
1247
  });
947
1248
 
@@ -990,6 +1291,8 @@ function resolveFontProperties(theme, fontRef) {
990
1291
  size: fontDef.size,
991
1292
  color: fontDef.color,
992
1293
  bold: fontDef.bold,
1294
+ fontWeight: fontDef.fontWeight,
1295
+ case: fontDef.case,
993
1296
  italic: fontDef.italic,
994
1297
  underline: fontDef.underline,
995
1298
  alignment: fontDef.alignment,
@@ -1256,6 +1559,43 @@ var init_layoutUtils = __esm({
1256
1559
  }
1257
1560
  });
1258
1561
 
1562
+ // src/utils/generationContext.ts
1563
+ import { AsyncLocalStorage } from "async_hooks";
1564
+ import { isAbsolute, resolve } from "path";
1565
+ function runWithBaseDir(baseDir, callback) {
1566
+ return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
1567
+ }
1568
+ function resolveFromBaseDir(filePath) {
1569
+ const base = baseDirStorage.getStore();
1570
+ if (!base || isAbsolute(filePath)) return filePath;
1571
+ return resolve(base, filePath);
1572
+ }
1573
+ function runWithWarnings(warnings, callback) {
1574
+ return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
1575
+ }
1576
+ function reportWarning(component, code, message, context) {
1577
+ const warnings = warningsStorage.getStore();
1578
+ if (warnings) {
1579
+ warnings.push({
1580
+ component,
1581
+ message,
1582
+ severity: "warning",
1583
+ context: { code, ...context }
1584
+ });
1585
+ return;
1586
+ }
1587
+ console.warn(`[json-to-docx] [${code}] ${message}`);
1588
+ }
1589
+ var generationDateStorage, baseDirStorage, warningsStorage;
1590
+ var init_generationContext = __esm({
1591
+ "src/utils/generationContext.ts"() {
1592
+ "use strict";
1593
+ generationDateStorage = new AsyncLocalStorage();
1594
+ baseDirStorage = new AsyncLocalStorage();
1595
+ warningsStorage = new AsyncLocalStorage();
1596
+ }
1597
+ });
1598
+
1259
1599
  // src/themes/json/parser.ts
1260
1600
  import { validateThemeJson } from "@json-to-office/shared-docx/validation/unified";
1261
1601
  var ThemeValidationError, ThemeParseError, ThemeParser;
@@ -2072,6 +2412,9 @@ function runOptions(formatting) {
2072
2412
  options.underline = formatting.underline ? { type: formatting.underline.type } : void 0;
2073
2413
  }
2074
2414
  if (formatting.strike !== void 0) options.strike = formatting.strike;
2415
+ if (formatting.allCaps !== void 0) options.allCaps = formatting.allCaps;
2416
+ if (formatting.smallCaps !== void 0)
2417
+ options.smallCaps = formatting.smallCaps;
2075
2418
  if (formatting.scalePercent !== void 0) {
2076
2419
  options.scale = formatting.scalePercent;
2077
2420
  }
@@ -2762,6 +3105,44 @@ var init_styles2 = __esm({
2762
3105
  }
2763
3106
  });
2764
3107
 
3108
+ // src/utils/normalizeTextCase.ts
3109
+ function normalizeTextCase(zip) {
3110
+ let changed = false;
3111
+ for (const entry of zip.getEntries()) {
3112
+ if (!WORD_XML.test(entry.entryName)) continue;
3113
+ const xml = entry.getData().toString("utf8");
3114
+ if (!ANY_CASE_FLAG.test(xml)) continue;
3115
+ const normalized = xml.replace(RUN_PROPERTIES, completeCaseFlags);
3116
+ if (normalized === xml) continue;
3117
+ zip.updateFile(entry, Buffer.from(normalized, "utf8"));
3118
+ changed = true;
3119
+ }
3120
+ return changed;
3121
+ }
3122
+ function completeCaseFlags(runProperties3) {
3123
+ const flag = CASE_FLAG.exec(runProperties3);
3124
+ if (!flag) return runProperties3;
3125
+ const isCaps = flag[1] === "caps";
3126
+ const opposite = `<w:${isCaps ? "smallCaps" : "caps"} w:val="false"/>`;
3127
+ if (runProperties3.includes(`<w:${isCaps ? "smallCaps" : "caps"}`)) {
3128
+ return runProperties3;
3129
+ }
3130
+ return runProperties3.replace(
3131
+ flag[0],
3132
+ isCaps ? `${flag[0]}${opposite}` : `${opposite}${flag[0]}`
3133
+ );
3134
+ }
3135
+ var WORD_XML, ANY_CASE_FLAG, RUN_PROPERTIES, CASE_FLAG;
3136
+ var init_normalizeTextCase = __esm({
3137
+ "src/utils/normalizeTextCase.ts"() {
3138
+ "use strict";
3139
+ WORD_XML = /^word\/.*\.xml$/;
3140
+ ANY_CASE_FLAG = /<w:(?:caps|smallCaps)\b/;
3141
+ RUN_PROPERTIES = /<w:rPr\b[^>]*>[\s\S]*?<\/w:rPr>/g;
3142
+ CASE_FLAG = /<w:(caps|smallCaps)\b[^>]*(?:\/>|>[\s\S]*?<\/w:\1>)/;
3143
+ }
3144
+ });
3145
+
2765
3146
  // src/utils/packageDocument.ts
2766
3147
  import AdmZip from "adm-zip";
2767
3148
  function resolveGenerationDate(options) {
@@ -2830,6 +3211,7 @@ function canonicalizeRelationshipIds(zip) {
2830
3211
  }
2831
3212
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
2832
3213
  const zip = new AdmZip(buffer);
3214
+ normalizeTextCase(zip);
2833
3215
  canonicalizeRelationshipIds(zip);
2834
3216
  const isoTimestamp = generatedAt.toISOString();
2835
3217
  const coreProperties3 = zip.getEntry("docProps/core.xml");
@@ -2846,10 +3228,15 @@ function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
2846
3228
  }
2847
3229
  return zip.toBuffer();
2848
3230
  }
3231
+ function normalizeDocxCaseBuffer(buffer) {
3232
+ const zip = new AdmZip(buffer);
3233
+ return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
3234
+ }
2849
3235
  var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID;
2850
3236
  var init_packageDocument = __esm({
2851
3237
  "src/utils/packageDocument.ts"() {
2852
3238
  "use strict";
3239
+ init_normalizeTextCase();
2853
3240
  DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
2854
3241
  STABLE_RELATIONSHIP_ID = /^rId\d+$/;
2855
3242
  }
@@ -2908,7 +3295,8 @@ function createDocxJsRenderer() {
2908
3295
  const document = buildDocument(ir, resources);
2909
3296
  const packed = await Packer.toBuffer(document);
2910
3297
  const fixed = fixFloatingImageIdsInBuffer(packed);
2911
- if (renderOptions?.deterministic === false) return new Uint8Array(fixed);
3298
+ if (renderOptions?.deterministic === false)
3299
+ return new Uint8Array(normalizeDocxCaseBuffer(fixed));
2912
3300
  return new Uint8Array(
2913
3301
  canonicalizeDocxBuffer(
2914
3302
  fixed,
@@ -4187,7 +4575,8 @@ async function createOfficeOpenDocxRenderer() {
4187
4575
  spliceChartParts(zip, charts);
4188
4576
  raw = zip.toBuffer();
4189
4577
  }
4190
- if (options?.deterministic === false) return new Uint8Array(raw);
4578
+ if (options?.deterministic === false)
4579
+ return new Uint8Array(normalizeDocxCaseBuffer(raw));
4191
4580
  return new Uint8Array(
4192
4581
  canonicalizeDocxBuffer(
4193
4582
  raw,
@@ -4616,7 +5005,10 @@ async function resolveDocumentFonts(document, theme, fonts, warnings, forceMater
4616
5005
  }
4617
5006
 
4618
5007
  // src/core/generateFromIr.ts
4619
- import { toRasterizeFontFaces } from "@json-to-office/shared/fonts/node";
5008
+ import {
5009
+ toChartFontFaces,
5010
+ toRasterizeFontFaces
5011
+ } from "@json-to-office/shared/fonts/node";
4620
5012
 
4621
5013
  // src/core/prerasterizeVisuals.ts
4622
5014
  import {
@@ -5031,7 +5423,88 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
5031
5423
  }
5032
5424
  if (!outcome.applied) await rasterizeIndividually(chunk);
5033
5425
  }
5034
- return map;
5426
+ return map;
5427
+ }
5428
+
5429
+ // src/core/componentTransform.ts
5430
+ async function transformComponents(doc, replace) {
5431
+ return await walk(doc, replace);
5432
+ }
5433
+ async function walk(node, replace) {
5434
+ if (Array.isArray(node)) {
5435
+ return Promise.all(node.map((child) => walk(child, replace)));
5436
+ }
5437
+ if (!node || typeof node !== "object") return node;
5438
+ const obj = node;
5439
+ if (typeof obj.name === "string" && obj.props) {
5440
+ const replacement = await replace(obj);
5441
+ if (replacement) return replacement;
5442
+ }
5443
+ const next = { ...obj };
5444
+ if (Array.isArray(obj.children)) {
5445
+ next.children = await walk(obj.children, replace);
5446
+ }
5447
+ if (obj.props && typeof obj.props === "object") {
5448
+ const props = obj.props;
5449
+ let changed = false;
5450
+ const nextProps = { ...props };
5451
+ for (const key of ["header", "footer"]) {
5452
+ if (Array.isArray(props[key])) {
5453
+ nextProps[key] = await walk(props[key], replace);
5454
+ changed = true;
5455
+ }
5456
+ }
5457
+ if (Array.isArray(props.columns)) {
5458
+ nextProps.columns = await Promise.all(
5459
+ props.columns.map(
5460
+ (column) => walkColumn(column, replace)
5461
+ )
5462
+ );
5463
+ changed = true;
5464
+ }
5465
+ if (changed) next.props = nextProps;
5466
+ }
5467
+ return next;
5468
+ }
5469
+ async function walkColumn(column, replace) {
5470
+ if (!column || typeof column !== "object") return column;
5471
+ const source = column;
5472
+ const next = { ...source };
5473
+ const header = source.header;
5474
+ if (header && typeof header === "object" && "content" in header) {
5475
+ next.header = { ...header, content: await walk(header.content, replace) };
5476
+ }
5477
+ if (Array.isArray(source.cells)) {
5478
+ next.cells = await Promise.all(
5479
+ source.cells.map(async (cell) => {
5480
+ if (cell && typeof cell === "object" && "content" in cell) {
5481
+ const source2 = cell;
5482
+ return { ...source2, content: await walk(source2.content, replace) };
5483
+ }
5484
+ return cell;
5485
+ })
5486
+ );
5487
+ }
5488
+ return next;
5489
+ }
5490
+ function withNodeIdentity(original, replacement) {
5491
+ const out = { ...replacement };
5492
+ if (original.id !== void 0) out.id = original.id;
5493
+ if (original.enabled !== void 0) out.enabled = original.enabled;
5494
+ return out;
5495
+ }
5496
+ function containsComponent(root, name) {
5497
+ const seen = /* @__PURE__ */ new WeakSet();
5498
+ const visit = (node) => {
5499
+ if (!node || typeof node !== "object" || seen.has(node)) return false;
5500
+ seen.add(node);
5501
+ if (Array.isArray(node)) return node.some(visit);
5502
+ const obj = node;
5503
+ if (typeof obj.name === "string" && obj.enabled === false) return false;
5504
+ if (obj.name === name && obj.props !== void 0) return true;
5505
+ return Object.values(obj).some(visit);
5506
+ };
5507
+ return visit(root);
5035
5508
  }
5036
5509
 
5037
5510
  // src/core/desugarExternals.ts
@@ -5045,7 +5518,14 @@ import {
5045
5518
 
5046
5519
  // src/components/highcharts.ts
5047
5520
  init_colorUtils();
5048
- import { DEFAULT_CHART_THEME_COLORS } from "@json-to-office/shared";
5521
+ init_layoutUtils();
5522
+ import {
5523
+ chartFamilyResolver,
5524
+ chartPointsPerPixel,
5525
+ POINTS_PER_PIXEL_96DPI,
5526
+ withChartFontFaceCss,
5527
+ withChartTypography
5528
+ } from "@json-to-office/shared";
5049
5529
  var DEFAULT_EXPORT_SERVER_URL = "http://localhost:7801";
5050
5530
  async function generateChart(config, servicesConfig) {
5051
5531
  if (!isNodeEnvironment()) {
@@ -5099,19 +5579,83 @@ function toChartColor(value, theme) {
5099
5579
  function withThemeColors(config, theme) {
5100
5580
  const options = config.options;
5101
5581
  if (!options || options.colors || !theme?.colors) return config;
5102
- const themeColors = theme.colors;
5103
- const palette = DEFAULT_CHART_THEME_COLORS.map((token) => {
5104
- const value = themeColors[token];
5105
- return typeof value === "string" && value.length > 0 ? toChartColor(value, theme) : void 0;
5106
- }).filter((c) => c !== void 0);
5582
+ const palette = chartPaletteValues(theme).flatMap((value) => {
5583
+ const color = toChartColor(value, theme);
5584
+ return color ? [color] : [];
5585
+ });
5107
5586
  if (palette.length === 0) return config;
5108
5587
  return {
5109
5588
  ...config,
5110
5589
  options: { ...config.options, colors: palette }
5111
5590
  };
5112
5591
  }
5113
- async function renderChartToImageProps(props, theme, servicesConfig) {
5114
- const config = withThemeColors(props, theme);
5592
+ var POINTS_PER_TWIP = 1 / 20;
5593
+ var POINTS_PER_PIXEL = POINTS_PER_PIXEL_96DPI;
5594
+ function placedWidthPt(config, theme) {
5595
+ const { width, height } = config;
5596
+ const chart = config.options.chart;
5597
+ if (typeof width === "number") return width * POINTS_PER_PIXEL;
5598
+ if (typeof width === "string") {
5599
+ const page = getPageSetup(theme);
5600
+ const contentWidthPt = (page.size.width - page.margin.left - page.margin.right) * POINTS_PER_TWIP;
5601
+ return parseFloat(width) / 100 * contentWidthPt;
5602
+ }
5603
+ if (typeof height === "number" && chart.height > 0) {
5604
+ return height / chart.height * chart.width * POINTS_PER_PIXEL;
5605
+ }
5606
+ return void 0;
5607
+ }
5608
+ function toHex(token, theme) {
5609
+ return `#${resolveColor(token, theme)}`;
5610
+ }
5611
+ function themeChartTypography(theme) {
5612
+ const styles = theme.styles ?? {};
5613
+ const family = chartFamilyResolver(theme);
5614
+ const bodyPt = styles.normal?.size ?? theme.fonts.body.size ?? 11;
5615
+ const label = styles.chartLabel;
5616
+ const heading = styles.heading3;
5617
+ const labelPt = label?.size ?? Math.max(bodyPt - 1, 6);
5618
+ return {
5619
+ bodyFamily: family(theme.fonts.body.family),
5620
+ headingFamily: family(theme.fonts.heading.family),
5621
+ textColor: toHex("textPrimary", theme),
5622
+ mutedColor: toHex("textSecondary", theme),
5623
+ labelPt,
5624
+ labelWeight: label?.fontWeight ?? (label?.bold ? 700 : void 0),
5625
+ titlePt: heading?.size ?? bodyPt + 2,
5626
+ titleWeight: heading?.fontWeight ?? (heading?.bold === false ? void 0 : 700),
5627
+ sourcePt: styles.source?.size ?? Math.max(labelPt - 1, 6)
5628
+ };
5629
+ }
5630
+ function withThemeTypography(config, theme) {
5631
+ if (!theme?.fonts?.body?.family || !theme.fonts.heading?.family) {
5632
+ return config;
5633
+ }
5634
+ return {
5635
+ ...config,
5636
+ options: withChartTypography(
5637
+ config.options,
5638
+ themeChartTypography(theme),
5639
+ chartPointsPerPixel(
5640
+ config.options.chart.width,
5641
+ placedWidthPt(config, theme)
5642
+ )
5643
+ )
5644
+ };
5645
+ }
5646
+ function withChartFontFaces(config, theme, faces) {
5647
+ if (!faces?.length || !theme?.fonts) return config;
5648
+ return withChartFontFaceCss(config, faces, [
5649
+ theme.fonts.body.family,
5650
+ theme.fonts.heading.family
5651
+ ]);
5652
+ }
5653
+ async function renderChartToImageProps(props, theme, servicesConfig, chartFonts) {
5654
+ const config = withChartFontFaces(
5655
+ withThemeTypography(withThemeColors(props, theme), theme),
5656
+ theme,
5657
+ chartFonts
5658
+ );
5115
5659
  const chart = await generateChart(config, servicesConfig);
5116
5660
  const hasConfigDimensions = config.width !== void 0 || config.height !== void 0;
5117
5661
  return {
@@ -5122,74 +5666,6 @@ async function renderChartToImageProps(props, theme, servicesConfig) {
5122
5666
  };
5123
5667
  }
5124
5668
 
5125
- // src/core/componentTransform.ts
5126
- async function transformComponents(doc, replace) {
5127
- return await walk(doc, replace);
5128
- }
5129
- async function walk(node, replace) {
5130
- if (Array.isArray(node)) {
5131
- return Promise.all(node.map((child) => walk(child, replace)));
5132
- }
5133
- if (!node || typeof node !== "object") return node;
5134
- const obj = node;
5135
- if (typeof obj.name === "string" && obj.props) {
5136
- const replacement = await replace(obj);
5137
- if (replacement) return replacement;
5138
- }
5139
- const next = { ...obj };
5140
- if (Array.isArray(obj.children)) {
5141
- next.children = await walk(obj.children, replace);
5142
- }
5143
- if (obj.props && typeof obj.props === "object") {
5144
- const props = obj.props;
5145
- let changed = false;
5146
- const nextProps = { ...props };
5147
- for (const key of ["header", "footer"]) {
5148
- if (Array.isArray(props[key])) {
5149
- nextProps[key] = await walk(props[key], replace);
5150
- changed = true;
5151
- }
5152
- }
5153
- if (Array.isArray(props.columns)) {
5154
- nextProps.columns = await Promise.all(
5155
- props.columns.map(
5156
- (column) => walkColumn(column, replace)
5157
- )
5158
- );
5159
- changed = true;
5160
- }
5161
- if (changed) next.props = nextProps;
5162
- }
5163
- return next;
5164
- }
5165
- async function walkColumn(column, replace) {
5166
- if (!column || typeof column !== "object") return column;
5167
- const source = column;
5168
- const next = { ...source };
5169
- const header = source.header;
5170
- if (header && typeof header === "object" && "content" in header) {
5171
- next.header = { ...header, content: await walk(header.content, replace) };
5172
- }
5173
- if (Array.isArray(source.cells)) {
5174
- next.cells = await Promise.all(
5175
- source.cells.map(async (cell) => {
5176
- if (cell && typeof cell === "object" && "content" in cell) {
5177
- const source2 = cell;
5178
- return { ...source2, content: await walk(source2.content, replace) };
5179
- }
5180
- return cell;
5181
- })
5182
- );
5183
- }
5184
- return next;
5185
- }
5186
- function withNodeIdentity(original, replacement) {
5187
- const out = { ...replacement };
5188
- if (original.id !== void 0) out.id = original.id;
5189
- if (original.enabled !== void 0) out.enabled = original.enabled;
5190
- return out;
5191
- }
5192
-
5193
5669
  // src/core/desugarExternals.ts
5194
5670
  async function desugarExternals(document, options) {
5195
5671
  const prerastered = await prerasterizeVisuals(
@@ -5219,7 +5695,8 @@ async function desugarExternals(document, options) {
5219
5695
  props: await renderChartToImageProps(
5220
5696
  node.props,
5221
5697
  options.theme,
5222
- options.services?.highcharts
5698
+ options.services?.highcharts,
5699
+ options.chartFonts
5223
5700
  )
5224
5701
  });
5225
5702
  }
@@ -5349,10 +5826,7 @@ init_colorUtils();
5349
5826
  import {
5350
5827
  FeatureRequirementCollector
5351
5828
  } from "@json-to-office/shared/rendering";
5352
- import {
5353
- synthesizeFamilyName,
5354
- DEFAULT_CHART_THEME_COLORS as DEFAULT_CHART_THEME_COLORS2
5355
- } from "@json-to-office/shared";
5829
+ import { capsFormatting as capsFormatting2, synthesizeFamilyName as synthesizeFamilyName2 } from "@json-to-office/shared";
5356
5830
  import {
5357
5831
  isNativeVisualProps as isNativeVisualProps4
5358
5832
  } from "@json-to-office/shared-docx";
@@ -5868,6 +6342,7 @@ function resolveColors(entries, at, deps) {
5868
6342
  init_themes();
5869
6343
  init_colorUtils();
5870
6344
  init_styleHelpers();
6345
+ import { capsFormatting, synthesizeFamilyName } from "@json-to-office/shared";
5871
6346
  var RIGHT_MARGIN_TAB_TWIPS = 9026 * 2;
5872
6347
  var STATISTIC_NUMBER_STYLE_ID = "StatisticNumber";
5873
6348
  var STATISTIC_DESCRIPTION_STYLE_ID = "StatisticDescription";
@@ -5876,25 +6351,8 @@ var STATISTIC_SIZE_POINTS = {
5876
6351
  medium: 28,
5877
6352
  large: 40
5878
6353
  };
5879
- function isValidStyleName(name) {
5880
- return [
5881
- "normal",
5882
- "heading1",
5883
- "heading2",
5884
- "heading3",
5885
- "heading4",
5886
- "heading5",
5887
- "heading6",
5888
- "title",
5889
- "subtitle"
5890
- ].includes(name);
5891
- }
5892
6354
  function getStyleSafe(theme, styleName) {
5893
- if (!isValidStyleName(styleName)) {
5894
- console.warn(`Invalid style name: ${styleName}`);
5895
- return void 0;
5896
- }
5897
- return theme.styles?.[styleName];
6355
+ return theme.styles && Object.hasOwn(theme.styles, styleName) ? theme.styles[styleName] : void 0;
5898
6356
  }
5899
6357
  function resolveStyleWithBaseStyle(theme, styleName, visited = /* @__PURE__ */ new Set()) {
5900
6358
  if (visited.has(styleName)) {
@@ -5951,9 +6409,17 @@ function resolveSpacing(spacing) {
5951
6409
  ...spacing?.after ? { afterTwips: pointsToTwips(spacing.after) } : {}
5952
6410
  };
5953
6411
  }
6412
+ function weightedFamily(props) {
6413
+ return props.fontWeight === void 0 ? void 0 : synthesizeFamilyName(
6414
+ props.family || "Arial",
6415
+ props.fontWeight,
6416
+ props.italic === true
6417
+ );
6418
+ }
5954
6419
  function convertRunProperties(merged, theme, defaultColor, defaultSize) {
6420
+ const weighted = weightedFamily(merged);
5955
6421
  return {
5956
- fontFamily: merged.family || "Arial",
6422
+ fontFamily: weighted?.family ?? merged.family ?? "Arial",
5957
6423
  sizeHalfPoints: (merged.size || defaultSize || 11) * 2,
5958
6424
  color: {
5959
6425
  hex: resolveColor(
@@ -5963,6 +6429,8 @@ function convertRunProperties(merged, theme, defaultColor, defaultSize) {
5963
6429
  },
5964
6430
  ...merged.bold !== void 0 && { bold: merged.bold },
5965
6431
  ...merged.italic !== void 0 && { italic: merged.italic },
6432
+ ...weighted && { bold: weighted.bold, italic: weighted.italic },
6433
+ ...merged.case !== void 0 && capsFormatting(merged.case),
5966
6434
  ...merged.underline !== void 0 && merged.underline && { underline: { type: "single" } },
5967
6435
  ...merged.characterSpacing && {
5968
6436
  characterSpacingTwentieths: merged.characterSpacing.type === "condensed" ? -merged.characterSpacing.value : merged.characterSpacing.value
@@ -6035,12 +6503,15 @@ function convertBorders(borders3, theme) {
6035
6503
  return Object.keys(converted).length > 0 ? converted : void 0;
6036
6504
  }
6037
6505
  function fallbackRun(fontProps, theme, defaultColor, defaultSize) {
6506
+ const weighted = weightedFamily(fontProps);
6038
6507
  return {
6039
- fontFamily: fontProps.family || "Arial",
6508
+ fontFamily: weighted?.family ?? fontProps.family ?? "Arial",
6040
6509
  sizeHalfPoints: (fontProps.size || defaultSize) * 2,
6041
6510
  color: { hex: resolveColor(fontProps.color || defaultColor, theme) },
6042
6511
  ...fontProps.bold !== void 0 && { bold: fontProps.bold },
6043
6512
  ...fontProps.italic !== void 0 && { italic: fontProps.italic },
6513
+ ...weighted && { bold: weighted.bold, italic: weighted.italic },
6514
+ ...fontProps.case !== void 0 && capsFormatting(fontProps.case),
6044
6515
  ...fontProps.underline !== void 0 && fontProps.underline && { underline: { type: "single" } }
6045
6516
  };
6046
6517
  }
@@ -6070,6 +6541,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6070
6541
  size: normalStyle?.size,
6071
6542
  color: normalStyle?.color,
6072
6543
  bold: normalStyle?.bold,
6544
+ fontWeight: normalStyle?.fontWeight,
6545
+ case: normalStyle?.case,
6073
6546
  italic: normalStyle?.italic,
6074
6547
  underline: normalStyle?.underline,
6075
6548
  characterSpacing: normalStyle?.characterSpacing,
@@ -6109,6 +6582,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6109
6582
  size: headingStyle.size,
6110
6583
  color: headingStyle.color,
6111
6584
  bold: headingStyle.bold,
6585
+ fontWeight: headingStyle.fontWeight,
6586
+ case: headingStyle.case,
6112
6587
  italic: headingStyle.italic,
6113
6588
  underline: headingStyle.underline,
6114
6589
  characterSpacing: headingStyle.characterSpacing,
@@ -6169,6 +6644,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6169
6644
  size: headingStyle.size,
6170
6645
  color: headingStyle.color,
6171
6646
  bold: headingStyle.bold,
6647
+ fontWeight: headingStyle.fontWeight,
6648
+ case: headingStyle.case,
6172
6649
  italic: headingStyle.italic,
6173
6650
  underline: headingStyle.underline,
6174
6651
  characterSpacing: headingStyle.characterSpacing,
@@ -6230,6 +6707,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6230
6707
  size: titleStyle.size,
6231
6708
  color: titleStyle.color,
6232
6709
  bold: titleStyle.bold,
6710
+ fontWeight: titleStyle.fontWeight,
6711
+ case: titleStyle.case,
6233
6712
  italic: titleStyle.italic,
6234
6713
  underline: titleStyle.underline,
6235
6714
  characterSpacing: titleStyle.characterSpacing,
@@ -6285,6 +6764,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6285
6764
  size: subtitleStyle.size,
6286
6765
  color: subtitleStyle.color,
6287
6766
  bold: subtitleStyle.bold,
6767
+ fontWeight: subtitleStyle.fontWeight,
6768
+ case: subtitleStyle.case,
6288
6769
  italic: subtitleStyle.italic,
6289
6770
  underline: subtitleStyle.underline,
6290
6771
  characterSpacing: subtitleStyle.characterSpacing,
@@ -6407,6 +6888,8 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
6407
6888
  size: customStyle.size,
6408
6889
  color: customStyle.color,
6409
6890
  bold: customStyle.bold,
6891
+ fontWeight: customStyle.fontWeight,
6892
+ case: customStyle.case,
6410
6893
  italic: customStyle.italic,
6411
6894
  underline: customStyle.underline,
6412
6895
  characterSpacing: customStyle.characterSpacing,
@@ -7971,6 +8454,210 @@ function emptyToUndefined(formatting) {
7971
8454
  return entries.length === 0 ? void 0 : Object.fromEntries(entries);
7972
8455
  }
7973
8456
 
8457
+ // src/blocks/index.ts
8458
+ import { KEY_TAKEAWAYS_BUDGET } from "@json-to-office/shared-docx";
8459
+
8460
+ // src/blocks/keyTakeaways.ts
8461
+ var MIN_RULE_PT = 0.25;
8462
+ var MAX_RULE_PT = 12;
8463
+ var KEY_TAKEAWAYS_DEFAULT_LABEL = "Key takeaways";
8464
+ var FALLBACK_RECIPE = {
8465
+ ruleWeightPt: 1.5,
8466
+ ruleColor: "accent",
8467
+ padPt: 6,
8468
+ type: "label"
8469
+ };
8470
+ function compileKeyTakeaways(props, theme) {
8471
+ const recipe = theme.chrome?.keyTakeaways;
8472
+ const labelRole = recipe?.type ?? FALLBACK_RECIPE.type;
8473
+ const labelStyle = theme.styles?.[labelRole];
8474
+ const ruleWeightPt = Math.min(
8475
+ MAX_RULE_PT,
8476
+ Math.max(
8477
+ MIN_RULE_PT,
8478
+ recipe?.rule?.weightPt ?? FALLBACK_RECIPE.ruleWeightPt
8479
+ )
8480
+ );
8481
+ const ruleColor = recipe?.rule?.color ?? recipe?.color ?? FALLBACK_RECIPE.ruleColor;
8482
+ const padPt = recipe?.padPt ?? FALLBACK_RECIPE.padPt;
8483
+ const children = [
8484
+ {
8485
+ name: "divider",
8486
+ props: {
8487
+ thickness: ruleWeightPt,
8488
+ color: ruleColor,
8489
+ spacing: { before: 12, after: padPt }
8490
+ }
8491
+ },
8492
+ {
8493
+ name: "paragraph",
8494
+ props: {
8495
+ text: props.label ?? KEY_TAKEAWAYS_DEFAULT_LABEL,
8496
+ keepNext: true,
8497
+ spacing: { after: 4 },
8498
+ // The label role when the theme resolves one; a bold run in the
8499
+ // recipe's colour otherwise, so the label still reads as a label.
8500
+ ...labelStyle ? { themeStyle: labelRole } : {
8501
+ font: {
8502
+ bold: true,
8503
+ ...recipe?.color !== void 0 && { color: recipe.color }
8504
+ }
8505
+ }
8506
+ }
8507
+ },
8508
+ {
8509
+ name: "list",
8510
+ props: { items: [...props.items] }
8511
+ },
8512
+ {
8513
+ name: "divider",
8514
+ props: {
8515
+ thickness: 0.5,
8516
+ color: "borderPrimary",
8517
+ spacing: { before: padPt, after: 12 }
8518
+ }
8519
+ }
8520
+ ];
8521
+ return {
8522
+ children,
8523
+ sourceMap: {
8524
+ "/children/0": "",
8525
+ "/children/1": "",
8526
+ "/children/1/props/text": "/props/label",
8527
+ "/children/2": "",
8528
+ "/children/2/props/items": "/props/items",
8529
+ "/children/3": ""
8530
+ }
8531
+ };
8532
+ }
8533
+
8534
+ // src/blocks/index.ts
8535
+ var BLOCK_NAMES = ["key-takeaways"];
8536
+ var BLOCKS = {
8537
+ "key-takeaways": {
8538
+ compile: (props, theme) => compileKeyTakeaways(props, theme),
8539
+ budgets: (props, pointer) => (Array.isArray(props.items) ? props.items : []).flatMap(
8540
+ (item, index) => typeof item === "string" ? [
8541
+ {
8542
+ block: "key-takeaways",
8543
+ slot: "items",
8544
+ path: `${pointer}/props/items/${index}`,
8545
+ words: wordCount(item),
8546
+ maxWords: KEY_TAKEAWAYS_BUDGET.items.maxWords
8547
+ }
8548
+ ] : []
8549
+ )
8550
+ }
8551
+ };
8552
+ function isBlockName(name) {
8553
+ return BLOCK_NAMES.includes(name);
8554
+ }
8555
+ function isRecord(value) {
8556
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8557
+ }
8558
+ function expandBlocks(document, theme) {
8559
+ const sourceMap = {};
8560
+ const blocks = [];
8561
+ const walk2 = (node, pointer) => {
8562
+ if (Array.isArray(node)) {
8563
+ return node.map((child, index) => walk2(child, `${pointer}/${index}`));
8564
+ }
8565
+ if (!isRecord(node)) return node;
8566
+ if (typeof node.name === "string" && isRecord(node.props)) {
8567
+ if (node.enabled !== false && isBlockName(node.name)) {
8568
+ const compiled = BLOCKS[node.name].compile(node.props, theme);
8569
+ blocks.push(pointer);
8570
+ for (const [emitted, authored] of Object.entries(compiled.sourceMap)) {
8571
+ sourceMap[`${pointer}${emitted}`] = `${pointer}${authored}`;
8572
+ }
8573
+ return { ...node, children: compiled.children };
8574
+ }
8575
+ }
8576
+ const next = { ...node };
8577
+ if (isRecord(node.props)) {
8578
+ const props = { ...node.props };
8579
+ let changed = false;
8580
+ for (const key of ["header", "footer"]) {
8581
+ if (Array.isArray(props[key])) {
8582
+ props[key] = walk2(props[key], `${pointer}/props/${key}`);
8583
+ changed = true;
8584
+ }
8585
+ }
8586
+ if (Array.isArray(props.columns)) {
8587
+ props.columns = props.columns.map((column, index) => {
8588
+ if (!isRecord(column)) return column;
8589
+ const base = `${pointer}/props/columns/${index}`;
8590
+ const out = { ...column };
8591
+ if (isRecord(column.header) && "content" in column.header) {
8592
+ out.header = {
8593
+ ...column.header,
8594
+ content: walk2(column.header.content, `${base}/header/content`)
8595
+ };
8596
+ }
8597
+ if (Array.isArray(column.cells)) {
8598
+ out.cells = column.cells.map(
8599
+ (cell, cellIndex) => isRecord(cell) && "content" in cell ? {
8600
+ ...cell,
8601
+ content: walk2(
8602
+ cell.content,
8603
+ `${base}/cells/${cellIndex}/content`
8604
+ )
8605
+ } : cell
8606
+ );
8607
+ }
8608
+ return out;
8609
+ });
8610
+ changed = true;
8611
+ }
8612
+ if (changed) next.props = props;
8613
+ }
8614
+ if (Array.isArray(node.children)) {
8615
+ next.children = walk2(node.children, `${pointer}/children`);
8616
+ }
8617
+ return next;
8618
+ };
8619
+ const expanded = walk2(document, "");
8620
+ return {
8621
+ document: blocks.length > 0 ? expanded : document,
8622
+ sourceMap,
8623
+ blocks
8624
+ };
8625
+ }
8626
+ function toAuthoredPointer(sourceMap, pointer) {
8627
+ let best;
8628
+ for (const emitted of Object.keys(sourceMap)) {
8629
+ if ((pointer === emitted || pointer.startsWith(`${emitted}/`)) && (best === void 0 || emitted.length > best.length)) {
8630
+ best = emitted;
8631
+ }
8632
+ }
8633
+ if (best === void 0) return pointer;
8634
+ return `${sourceMap[best]}${pointer.slice(best.length)}`;
8635
+ }
8636
+ function blockSlotBudgets(document, blocks) {
8637
+ return blocks.flatMap((pointer) => {
8638
+ const node = nodeAt(document, pointer);
8639
+ if (!isRecord(node) || !isRecord(node.props) || !isBlockName(node.name)) {
8640
+ return [];
8641
+ }
8642
+ return BLOCKS[node.name].budgets(node.props, pointer);
8643
+ });
8644
+ }
8645
+ function wordCount(text) {
8646
+ const trimmed = text.trim();
8647
+ return trimmed === "" ? 0 : trimmed.split(/\s+/).length;
8648
+ }
8649
+ function nodeAt(root, pointer) {
8650
+ if (pointer === "") return root;
8651
+ let current = root;
8652
+ for (const segment of pointer.slice(1).split("/")) {
8653
+ const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
8654
+ if (Array.isArray(current)) current = current[Number(key)];
8655
+ else if (isRecord(current)) current = current[key];
8656
+ else return void 0;
8657
+ }
8658
+ return current;
8659
+ }
8660
+
7974
8661
  // src/ir/compiler.ts
7975
8662
  init_types();
7976
8663
  init_units();
@@ -8282,6 +8969,7 @@ function compileChromeParagraph(component, scope) {
8282
8969
  italic: font.italic ?? false,
8283
8970
  ...font.underline !== void 0 ? { underline: font.underline } : {},
8284
8971
  ...font.fontWeight !== void 0 ? { fontWeight: font.fontWeight } : {},
8972
+ ...font.case !== void 0 ? { case: font.case } : {},
8285
8973
  ...font.lineSpacing !== void 0 ? { lineSpacing: font.lineSpacing } : {}
8286
8974
  }
8287
8975
  };
@@ -8381,6 +9069,10 @@ function compileComponent(component, scope) {
8381
9069
  case "table":
8382
9070
  return compileTable(component, scope);
8383
9071
  default:
9072
+ if (isBlockName(component.name)) {
9073
+ if (component.enabled === false) return [];
9074
+ return compileBlock(component, scope);
9075
+ }
8384
9076
  scope.ctx.unsupported.push({
8385
9077
  name: component.name,
8386
9078
  path: scope.path
@@ -8388,6 +9080,20 @@ function compileComponent(component, scope) {
8388
9080
  return [];
8389
9081
  }
8390
9082
  }
9083
+ function compileBlock(component, scope) {
9084
+ const children = component.children ?? [];
9085
+ if (children.length === 0) {
9086
+ scope.ctx.unsupported.push({ name: component.name, path: scope.path });
9087
+ return [];
9088
+ }
9089
+ return children.flatMap(
9090
+ (child, index) => compileComponent(child, {
9091
+ ...scope,
9092
+ path: `${scope.path}.children[${index}]`,
9093
+ id: `${scope.id}:b${index}`
9094
+ })
9095
+ );
9096
+ }
8391
9097
  var MARKDOWN_LIST_LEVELS = {
8392
9098
  unordered: [
8393
9099
  { level: 0, format: "bullet", text: "\u2022", alignment: "left" },
@@ -9585,7 +10291,11 @@ function isoDateTime(date) {
9585
10291
  }
9586
10292
  function runFormatting3(font, props, ctx) {
9587
10293
  const hasWeightRequest = font.fontWeight != null || font.bold === true;
9588
- const effectiveFamily = font.family ?? (hasWeightRequest ? resolveBodyFamily(ctx.theme) : void 0);
10294
+ const roleName = props.themeStyle;
10295
+ const role = roleName ? ctx.theme.typography?.roles?.[roleName] : void 0;
10296
+ const roleStyles = ctx.theme.styles;
10297
+ const roleFamily = role && roleName ? resolveFontFamily(ctx.theme, roleStyles?.[roleName]?.font ?? role.face) : void 0;
10298
+ const effectiveFamily = font.family ?? (hasWeightRequest ? roleFamily ?? resolveBodyFamily(ctx.theme) : void 0);
9589
10299
  const weighted = applyFontWeightAlias({
9590
10300
  fontFamily: effectiveFamily,
9591
10301
  bold: font.bold,
@@ -9593,7 +10303,7 @@ function runFormatting3(font, props, ctx) {
9593
10303
  fontWeight: font.fontWeight
9594
10304
  });
9595
10305
  const formatting = {};
9596
- if (font.family || weighted.font && weighted.font !== effectiveFamily) {
10306
+ if (font.family || hasWeightRequest && roleFamily || weighted.font && weighted.font !== effectiveFamily) {
9597
10307
  formatting.fontFamily = weighted.font;
9598
10308
  }
9599
10309
  if (font.size) formatting.sizeHalfPoints = pointsToHalfPoints(font.size);
@@ -9605,6 +10315,8 @@ function runFormatting3(font, props, ctx) {
9605
10315
  formatting.underline = font.underline ? { type: "single" } : void 0;
9606
10316
  }
9607
10317
  if (font.scale) formatting.scalePercent = font.scale;
10318
+ if (font.case !== void 0)
10319
+ Object.assign(formatting, capsFormatting2(font.case));
9608
10320
  if (font.characterSpacing) {
9609
10321
  const { type, value } = font.characterSpacing;
9610
10322
  formatting.characterSpacingTwentieths = type === "condensed" ? -value : value;
@@ -9618,7 +10330,7 @@ function applyFontWeightAlias(opts) {
9618
10330
  return { bold: opts.bold, italic: opts.italic };
9619
10331
  }
9620
10332
  const weight = opts.fontWeight ?? (opts.bold === true ? 700 : void 0);
9621
- const synth = synthesizeFamilyName(
10333
+ const synth = synthesizeFamilyName2(
9622
10334
  opts.fontFamily,
9623
10335
  weight,
9624
10336
  opts.italic === true
@@ -9957,15 +10669,13 @@ function compileChart(component, scope) {
9957
10669
  }
9958
10670
  const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
9959
10671
  (color) => resolveColor(String(color), ctx.theme)
9960
- ) : DEFAULT_CHART_THEME_COLORS2.map((token) => {
9961
- const value = ctx.theme?.colors?.[token];
9962
- if (typeof value !== "string" || value.length === 0) return void 0;
10672
+ ) : chartPaletteValues(ctx.theme).flatMap((value) => {
9963
10673
  try {
9964
- return resolveColor(token, ctx.theme);
10674
+ return [resolveColor(value, ctx.theme)];
9965
10675
  } catch {
9966
- return void 0;
10676
+ return [];
9967
10677
  }
9968
- }).filter((color) => color !== void 0);
10678
+ });
9969
10679
  const page = getPageSetup(ctx.theme);
9970
10680
  const contentWidthInches = (page.size.width - page.margin.left - page.margin.right) / 1440;
9971
10681
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
@@ -11310,6 +12020,7 @@ import {
11310
12020
  normalizeHighchartsChart
11311
12021
  } from "@json-to-office/quality";
11312
12022
  import { DEFAULT_DOCX_RENDERER_ID as DEFAULT_DOCX_RENDERER_ID2 } from "@json-to-office/shared-docx";
12023
+ import { designColors as designColors2, resolveDesignColor as resolveDesignColor2 } from "@json-to-office/shared";
11313
12024
 
11314
12025
  // src/core/generationContext.ts
11315
12026
  import { applyExportMode } from "@json-to-office/shared";
@@ -11333,6 +12044,7 @@ function resolveBuiltInTheme(themeName, options = {}) {
11333
12044
  }
11334
12045
 
11335
12046
  // src/themes/overrides.ts
12047
+ import { mergeWithDefaults as mergeWithDefaults3 } from "@json-to-office/shared";
11336
12048
  function applyThemeOverrides(theme, overrides) {
11337
12049
  if (!overrides) return theme;
11338
12050
  const fonts = { ...theme.fonts };
@@ -11346,12 +12058,87 @@ function applyThemeOverrides(theme, overrides) {
11346
12058
  }
11347
12059
  return {
11348
12060
  ...theme,
12061
+ ...Object.fromEntries(
12062
+ ["palette", "typography", "spacing", "chrome", "motif"].flatMap(
12063
+ (name) => {
12064
+ const key = name;
12065
+ return overrides[key] === void 0 ? [] : [[key, mergeWithDefaults3(overrides[key], theme[key] ?? {})]];
12066
+ }
12067
+ )
12068
+ ),
11349
12069
  colors: { ...theme.colors, ...overrides.colors ?? {} },
11350
12070
  fonts,
11351
12071
  ...Object.keys(styles).length > 0 && { styles }
11352
12072
  };
11353
12073
  }
11354
12074
 
12075
+ // src/themes/design-system.ts
12076
+ init_defaults();
12077
+ import {
12078
+ designCanvas,
12079
+ resolveTypeRoles,
12080
+ validateDesignColors
12081
+ } from "@json-to-office/shared";
12082
+ function resolveDocxDesignSystem(theme) {
12083
+ validateDesignColors(theme, getThemeColors(theme));
12084
+ if (!theme.typography && !theme.spacing) return theme;
12085
+ const canvas = designCanvas("docx", theme.page.size);
12086
+ const roles = resolveTypeRoles(theme, canvas, theme.fonts.body.size ?? 11);
12087
+ const styles = { ...theme.styles };
12088
+ for (const [name, role] of Object.entries(roles)) {
12089
+ const spacing = {
12090
+ ...role.spaceBefore !== void 0 && { before: role.spaceBefore },
12091
+ ...role.spaceAfter !== void 0 && { after: role.spaceAfter }
12092
+ };
12093
+ styles[name] = {
12094
+ font: role.face ?? "body",
12095
+ size: role.size,
12096
+ ...role.weight !== void 0 && { fontWeight: role.weight },
12097
+ ...role.color !== void 0 && { color: role.color },
12098
+ ...role.case !== void 0 && { case: role.case },
12099
+ ...role.lineHeight !== void 0 && {
12100
+ lineSpacing: { type: "multiple", value: role.lineHeight }
12101
+ },
12102
+ ...role.tracking !== void 0 && {
12103
+ characterSpacing: {
12104
+ type: role.tracking < 0 ? "condensed" : "expanded",
12105
+ value: Math.abs(role.tracking) * role.size / 5
12106
+ }
12107
+ },
12108
+ ...Object.keys(spacing).length && { spacing },
12109
+ ...styles[name]
12110
+ };
12111
+ }
12112
+ const gap = theme.spacing?.blockGap?.normal ?? theme.spacing?.basePt;
12113
+ const scale = theme.typography?.scale?.[canvas];
12114
+ if (scale || gap !== void 0) {
12115
+ styles.normal = {
12116
+ ...scale && { size: scale.base },
12117
+ ...gap !== void 0 && { spacing: { after: gap } },
12118
+ ...styles.normal
12119
+ };
12120
+ }
12121
+ const space = theme.spacing?.canvas?.[canvas];
12122
+ const safe = space?.safeAreaIn;
12123
+ return {
12124
+ ...theme,
12125
+ styles,
12126
+ page: {
12127
+ ...theme.page,
12128
+ margins: {
12129
+ ...theme.page.margins,
12130
+ ...safe !== void 0 && {
12131
+ top: safe * 1440,
12132
+ bottom: safe * 1440,
12133
+ left: safe * 1440,
12134
+ right: safe * 1440
12135
+ },
12136
+ ...space?.gutterIn !== void 0 && { gutter: space.gutterIn * 1440 }
12137
+ }
12138
+ }
12139
+ };
12140
+ }
12141
+
11355
12142
  // src/core/generationContext.ts
11356
12143
  function defaultNamedThemeLookup(themeName, customThemes, warnings) {
11357
12144
  if (customThemes) {
@@ -11384,6 +12171,7 @@ function resolveThemeContext(documentIn, options = {}) {
11384
12171
  if (themeOverrides) {
11385
12172
  theme = applyThemeOverrides(theme, themeOverrides);
11386
12173
  }
12174
+ theme = resolveDocxDesignSystem(theme);
11387
12175
  const mode = applyExportMode({ doc: document, theme, fonts });
11388
12176
  for (const w of mode.warnings) {
11389
12177
  if (warnings) {
@@ -11912,11 +12700,13 @@ function walkActive(node, path4, page, visit) {
11912
12700
  );
11913
12701
  }
11914
12702
  function prepareDocxQualityDocument(document, options = {}) {
11915
- const context = options.context ?? resolveThemeContext(normalizeDocument(document)[0], {
12703
+ const themed = options.context ?? resolveThemeContext(normalizeDocument(document)[0], {
11916
12704
  customThemes: options.customThemes,
11917
12705
  fonts: options.fonts,
11918
12706
  warnings: options.warnings
11919
12707
  });
12708
+ const expanded = expandBlocks(themed.document, themed.theme);
12709
+ const context = expanded.blocks.length > 0 ? { ...themed, document: expanded.document } : themed;
11920
12710
  const resolved = resolveDocumentTree(context.document, context.theme);
11921
12711
  const basePage = pageBox(resolved.theme, context.themeName);
11922
12712
  const typography = {
@@ -11926,24 +12716,50 @@ function prepareDocxQualityDocument(document, options = {}) {
11926
12716
  const facts = [];
11927
12717
  const provenance = {};
11928
12718
  let previousHeadingLevel;
11929
- const addFact = (fact) => {
12719
+ const authoredPath = (path4) => toAuthoredPointer(expanded.sourceMap, path4);
12720
+ const addFact = (raw) => {
12721
+ const fact = {
12722
+ ...raw,
12723
+ path: authoredPath(raw.path),
12724
+ ...raw.relatedPaths && {
12725
+ relatedPaths: raw.relatedPaths.map(authoredPath)
12726
+ }
12727
+ };
11930
12728
  facts.push(fact);
11931
12729
  provenance[fact.id] = {
11932
12730
  path: fact.path,
11933
12731
  ...fact.relatedPaths && { relatedPaths: fact.relatedPaths }
11934
12732
  };
11935
12733
  };
12734
+ for (const budget of blockSlotBudgets(context.document, expanded.blocks)) {
12735
+ addFact({
12736
+ id: `docx:block-slot:${budget.path}`,
12737
+ kind: "docx/block-slot",
12738
+ ...budget
12739
+ });
12740
+ }
11936
12741
  const paletteHexes = {};
11937
- for (const [token, value] of Object.entries(
11938
- resolved.theme.colors ?? {}
11939
- )) {
12742
+ const visualColors = designColors2(
12743
+ resolved.theme.colors,
12744
+ resolved.theme.palette
12745
+ );
12746
+ const entries = {
12747
+ ...visualColors,
12748
+ ...Object.fromEntries(
12749
+ (resolved.theme.palette?.chart ?? []).map((value) => {
12750
+ const hex = resolveDesignColor2(value, visualColors);
12751
+ return [hex ? `#${hex}` : value, value];
12752
+ })
12753
+ )
12754
+ };
12755
+ for (const [token, value] of Object.entries(entries)) {
11940
12756
  if (typeof value !== "string") continue;
11941
- const hex = normalizeHex(value);
12757
+ const hex = normalizeHex(resolveDesignColor2(value, visualColors) ?? value);
11942
12758
  if (hex) paletteHexes[token] = hex;
11943
12759
  }
11944
- const paletteTokens = SERIES_COLOR_TOKENS.filter(
11945
- (token) => paletteHexes[token] !== void 0
11946
- );
12760
+ const paletteTokens = resolved.theme.palette?.chart?.map(
12761
+ (value) => `#${resolveDesignColor2(value, visualColors)}`
12762
+ ) ?? SERIES_COLOR_TOKENS.filter((token) => paletteHexes[token] !== void 0);
11947
12763
  const authoredPropsAt = (pointer) => asRecord(asRecord(nodeAtPointer(context.document, pointer))?.props);
11948
12764
  addFact({
11949
12765
  id: "docx:theme",
@@ -12064,7 +12880,16 @@ function prepareDocxQualityDocument(document, options = {}) {
12064
12880
  },
12065
12881
  facts,
12066
12882
  provenance,
12067
- renderer: options.renderer ?? DEFAULT_DOCX_RENDERER_ID2
12883
+ renderer: options.renderer ?? DEFAULT_DOCX_RENDERER_ID2,
12884
+ ...expanded.blocks.length > 0 && {
12885
+ metadata: {
12886
+ blocks: {
12887
+ sourceMap: expanded.sourceMap,
12888
+ blocks: expanded.blocks,
12889
+ document: context.document
12890
+ }
12891
+ }
12892
+ }
12068
12893
  };
12069
12894
  }
12070
12895
 
@@ -12104,19 +12929,22 @@ async function compileDocumentScoped(document, options, warnings) {
12104
12929
  });
12105
12930
  const { context } = prepared.model;
12106
12931
  const hasVisual = collectVisualProps(context.document).length > 0;
12932
+ const hasHighcharts = containsComponent(context.document, "highcharts");
12107
12933
  const resolvedFonts = await resolveDocumentFonts(
12108
12934
  context.document,
12109
12935
  context.theme,
12110
12936
  options.fonts,
12111
12937
  warnings,
12112
- hasVisual
12938
+ hasVisual || hasHighcharts
12113
12939
  );
12114
12940
  const visualFonts = hasVisual ? toRasterizeFontFaces(resolvedFonts, warnings) : [];
12941
+ const chartFonts = hasHighcharts ? toChartFontFaces(resolvedFonts) : [];
12115
12942
  const desugared = await desugarExternals(context.document, {
12116
12943
  theme: context.theme,
12117
12944
  ...options.services ? { services: options.services } : {},
12118
12945
  ...options.baseDir !== void 0 ? { baseDir: options.baseDir } : {},
12119
- ...visualFonts.length > 0 ? { visualFonts } : {}
12946
+ ...visualFonts.length > 0 ? { visualFonts } : {},
12947
+ ...chartFonts.length > 0 ? { chartFonts } : {}
12120
12948
  });
12121
12949
  const resolved = desugared === context.document ? prepared.model.document : resolveDocumentTree(desugared, context.theme);
12122
12950
  const structure = await processResolvedDocument(
@@ -12932,6 +13760,28 @@ var docxPlaceholderRule = {
12932
13760
  })
12933
13761
  )
12934
13762
  };
13763
+ var docxSlotBudgetRule = {
13764
+ id: "docx/slot-budget",
13765
+ code: QUALITY_CODES.SLOT_BUDGET,
13766
+ category: "composition",
13767
+ defaultSeverity: "warning",
13768
+ defaultCertainty: "deterministic",
13769
+ formats: ["docx"],
13770
+ evaluate: ({ facts }) => facts.filter(
13771
+ (fact) => fact.kind === "docx/block-slot"
13772
+ ).filter((fact) => fact.words > fact.maxWords).map((fact) => ({
13773
+ path: fact.path,
13774
+ message: `${fact.block} ${fact.slot} entry runs to ${fact.words} words; the slot holds ${fact.maxWords} \u2014 one claim, one sentence.`,
13775
+ suggestion: "Cut it to the conclusion. Move the evidence into the body it summarises.",
13776
+ evidence: {
13777
+ summary: "words in the slot against its budget",
13778
+ actual: fact.words,
13779
+ expected: fact.maxWords,
13780
+ unit: "words"
13781
+ },
13782
+ context: { block: fact.block, slot: fact.slot }
13783
+ }))
13784
+ };
12935
13785
  var DEFAULT_MAX_FONT_FAMILIES = 3;
12936
13786
  var docxFontCountRule = {
12937
13787
  id: "docx/font-count",
@@ -13097,6 +13947,7 @@ var DOCX_QUALITY_RULES = {
13097
13947
  docxSvgTextBoundsRule,
13098
13948
  docxLineBoxRule,
13099
13949
  docxPlaceholderRule,
13950
+ docxSlotBudgetRule,
13100
13951
  docxChartRule,
13101
13952
  docxTableDesignRule,
13102
13953
  docxFontCountRule,
@@ -14104,6 +14955,7 @@ function getCoreVersion() {
14104
14955
  return "Core v1.0.0";
14105
14956
  }
14106
14957
  export {
14958
+ BLOCK_NAMES,
14107
14959
  ComponentValidationError2 as ComponentValidationError,
14108
14960
  DocumentGenerator as CoreDocumentGenerator,
14109
14961
  DEFAULT_DOCX_RENDERER_ID,
@@ -14116,7 +14968,10 @@ export {
14116
14968
  UncompiledComponentError,
14117
14969
  UnknownPreservedComponentError,
14118
14970
  analyzeDocxQuality,
14971
+ blockSlotBudgets,
14119
14972
  cleanComponentProps,
14973
+ compileKeyTakeaways,
14974
+ consultingTheme,
14120
14975
  createComponent,
14121
14976
  createDocumentGenerator,
14122
14977
  createMinimalTheme,
@@ -14126,6 +14981,7 @@ export {
14126
14981
  docxRendererIds,
14127
14982
  docxRendererStatuses,
14128
14983
  examples,
14984
+ expandBlocks,
14129
14985
  exportPluginSchema,
14130
14986
  exportThemeToJson,
14131
14987
  flattenVisuals,
@@ -14169,6 +15025,7 @@ export {
14169
15025
  resolveDocxQualityProfile,
14170
15026
  runExample,
14171
15027
  themes,
15028
+ toAuthoredPointer,
14172
15029
  validateComponentProps,
14173
15030
  validateDocument,
14174
15031
  validateJsonSchema,