@json-to-office/core-docx 0.21.0 → 0.23.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 (32) hide show
  1. package/dist/components/highcharts.d.ts.map +1 -1
  2. package/dist/components/toc/index.d.ts.map +1 -1
  3. package/dist/core/content.d.ts.map +1 -1
  4. package/dist/core/generator.d.ts.map +1 -1
  5. package/dist/core/render.d.ts.map +1 -1
  6. package/dist/core/structure.d.ts +3 -0
  7. package/dist/core/structure.d.ts.map +1 -1
  8. package/dist/index.js +430 -105
  9. package/dist/index.js.map +1 -1
  10. package/dist/plugin/createDocumentGenerator.d.ts.map +1 -1
  11. package/dist/plugin/example/index.js +427 -48
  12. package/dist/plugin/example/index.js.map +1 -1
  13. package/dist/styles/theme-resolver.d.ts +14 -2
  14. package/dist/styles/theme-resolver.d.ts.map +1 -1
  15. package/dist/styles/theme-validator.d.ts.map +1 -1
  16. package/dist/styles/utils/colorUtils.d.ts +4 -3
  17. package/dist/styles/utils/colorUtils.d.ts.map +1 -1
  18. package/dist/styles/utils/resolveComponentTree.d.ts +3 -0
  19. package/dist/styles/utils/resolveComponentTree.d.ts.map +1 -1
  20. package/dist/templates/themes/apex.docx.theme.json +1 -6
  21. package/dist/templates/themes/corporate.docx.theme.json +7 -7
  22. package/dist/templates/themes/devportal.docx.theme.json +1 -6
  23. package/dist/templates/themes/index.d.ts +13 -1
  24. package/dist/templates/themes/index.d.ts.map +1 -1
  25. package/dist/templates/themes/minimal.docx.theme.json +1 -6
  26. package/dist/templates/themes/modern.docx.theme.json +1 -6
  27. package/dist/themes/defaults.d.ts +3 -0
  28. package/dist/themes/defaults.d.ts.map +1 -1
  29. package/dist/themes/json/index.d.ts +7 -6
  30. package/dist/themes/json/index.d.ts.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -368,12 +368,7 @@ var init_minimal_docx_theme = __esm({
368
368
  },
369
369
  componentDefaults: {
370
370
  table: {
371
- borders: false,
372
- striped: false,
373
- headerBackground: "#fafafa",
374
- headerColor: "#000000",
375
- borderColor: "#f0f0f0",
376
- borderWidth: 0.5
371
+ borderColor: "#f0f0f0"
377
372
  },
378
373
  list: {
379
374
  format: "bullet",
@@ -456,7 +451,12 @@ var init_corporate_docx_theme = __esm({
456
451
  keepNext: true,
457
452
  alignment: "left",
458
453
  borders: {
459
- bottom: { style: "single", size: 2, color: "primary", space: 6 }
454
+ bottom: {
455
+ style: "single",
456
+ size: 2,
457
+ color: "primary",
458
+ space: 6
459
+ }
460
460
  }
461
461
  },
462
462
  heading2: {
@@ -496,12 +496,7 @@ var init_corporate_docx_theme = __esm({
496
496
  },
497
497
  componentDefaults: {
498
498
  table: {
499
- borders: true,
500
- striped: true,
501
- headerBackground: "#1a365d",
502
- headerColor: "#FFFFFF",
503
- borderColor: "#E2E8F0",
504
- borderWidth: 0.5
499
+ borderColor: "#E2E8F0"
505
500
  },
506
501
  list: {
507
502
  format: "bullet",
@@ -618,12 +613,7 @@ var init_modern_docx_theme = __esm({
618
613
  },
619
614
  componentDefaults: {
620
615
  table: {
621
- borders: true,
622
- striped: false,
623
- headerBackground: "#6D28D9",
624
- headerColor: "#FFFFFF",
625
- borderColor: "#E2E8F0",
626
- borderWidth: 0.5
616
+ borderColor: "#E2E8F0"
627
617
  },
628
618
  list: {
629
619
  format: "bullet",
@@ -644,10 +634,277 @@ var init_modern_docx_theme = __esm({
644
634
  }
645
635
  });
646
636
 
637
+ // src/templates/themes/apex.docx.theme.json
638
+ var apex_docx_theme_default;
639
+ var init_apex_docx_theme = __esm({
640
+ "src/templates/themes/apex.docx.theme.json"() {
641
+ apex_docx_theme_default = {
642
+ name: "apex",
643
+ displayName: "Apex Consulting",
644
+ description: "A premium consulting theme with deep navy, warm amber accents, and authoritative typography",
645
+ version: "2.0.0",
646
+ colors: {
647
+ primary: "#0C2340",
648
+ secondary: "#1B3A5C",
649
+ accent: "#D4A843",
650
+ text: "#1A1A2E",
651
+ background: "#FFFFFF",
652
+ border: "#D6DAE0",
653
+ textPrimary: "#1A1A2E",
654
+ textSecondary: "#4A5066",
655
+ textMuted: "#8B91A0",
656
+ borderPrimary: "#B8BCC6",
657
+ borderSecondary: "#E8EAF0",
658
+ backgroundPrimary: "#FFFFFF",
659
+ backgroundSecondary: "#F4F5F8"
660
+ },
661
+ fonts: {
662
+ heading: { family: "Georgia", size: 26 },
663
+ body: { family: "Calibri", size: 11 },
664
+ mono: { family: "Consolas", size: 10 },
665
+ light: { family: "Georgia", size: 26 }
666
+ },
667
+ page: {
668
+ size: "A4",
669
+ margins: {
670
+ top: 1440,
671
+ bottom: 1440,
672
+ left: 1200,
673
+ right: 1200,
674
+ header: 720,
675
+ footer: 720,
676
+ gutter: 0
677
+ }
678
+ },
679
+ styles: {
680
+ normal: {
681
+ font: "body",
682
+ color: "textPrimary",
683
+ lineSpacing: { type: "multiple", value: 1.4 },
684
+ alignment: "justify",
685
+ spacing: { after: 8 }
686
+ },
687
+ heading1: {
688
+ font: "heading",
689
+ size: 26,
690
+ bold: false,
691
+ color: "primary",
692
+ spacing: { before: 24, after: 12 },
693
+ keepNext: true,
694
+ alignment: "left",
695
+ borders: {
696
+ bottom: {
697
+ style: "single",
698
+ size: 2,
699
+ color: "accent",
700
+ space: 6
701
+ }
702
+ }
703
+ },
704
+ heading2: {
705
+ font: "heading",
706
+ size: 18,
707
+ bold: false,
708
+ color: "secondary",
709
+ spacing: { before: 18, after: 8 },
710
+ keepNext: true,
711
+ alignment: "left"
712
+ },
713
+ heading3: {
714
+ font: "body",
715
+ size: 13,
716
+ bold: true,
717
+ color: "accent",
718
+ spacing: { before: 12, after: 6 },
719
+ keepNext: true,
720
+ alignment: "left"
721
+ },
722
+ title: {
723
+ font: "light",
724
+ size: 36,
725
+ bold: false,
726
+ color: "primary",
727
+ alignment: "left",
728
+ spacing: { before: 0, after: 18 }
729
+ },
730
+ subtitle: {
731
+ font: "body",
732
+ size: 16,
733
+ italic: false,
734
+ color: "textSecondary",
735
+ alignment: "left",
736
+ spacing: { before: 0, after: 24 }
737
+ }
738
+ },
739
+ componentDefaults: {
740
+ table: {
741
+ borderColor: "#D6DAE0"
742
+ },
743
+ list: {
744
+ format: "bullet",
745
+ bullet: "\u2022",
746
+ indent: 3
747
+ },
748
+ image: {
749
+ alignment: "center"
750
+ },
751
+ statistic: {
752
+ alignment: "center"
753
+ },
754
+ section: {
755
+ pageBreak: false
756
+ },
757
+ heading: {
758
+ numbering: false
759
+ }
760
+ }
761
+ };
762
+ }
763
+ });
764
+
765
+ // src/templates/themes/devportal.docx.theme.json
766
+ var devportal_docx_theme_default;
767
+ var init_devportal_docx_theme = __esm({
768
+ "src/templates/themes/devportal.docx.theme.json"() {
769
+ devportal_docx_theme_default = {
770
+ name: "devportal",
771
+ displayName: "Developer Portal",
772
+ description: "A modern developer-focused theme with cool grays, teal accents, and monospace code styling",
773
+ version: "2.0.0",
774
+ colors: {
775
+ primary: "#0F172A",
776
+ secondary: "#334155",
777
+ accent: "#0D9488",
778
+ text: "#0F172A",
779
+ background: "#FFFFFF",
780
+ border: "#E2E8F0",
781
+ textPrimary: "#0F172A",
782
+ textSecondary: "#475569",
783
+ textMuted: "#94A3B8",
784
+ borderPrimary: "#CBD5E1",
785
+ borderSecondary: "#F1F5F9",
786
+ backgroundPrimary: "#FFFFFF",
787
+ backgroundSecondary: "#F8FAFC"
788
+ },
789
+ fonts: {
790
+ heading: { family: "Helvetica", size: 24 },
791
+ body: { family: "Calibri", size: 11 },
792
+ mono: { family: "Consolas", size: 10 },
793
+ light: { family: "Helvetica", size: 24 }
794
+ },
795
+ page: {
796
+ size: "A4",
797
+ margins: {
798
+ top: 1440,
799
+ bottom: 1440,
800
+ left: 1080,
801
+ right: 1080,
802
+ header: 720,
803
+ footer: 720,
804
+ gutter: 0
805
+ }
806
+ },
807
+ styles: {
808
+ normal: {
809
+ font: "body",
810
+ color: "textPrimary",
811
+ lineSpacing: { type: "multiple", value: 1.5 },
812
+ alignment: "left",
813
+ spacing: { after: 8 }
814
+ },
815
+ heading1: {
816
+ font: "heading",
817
+ size: 24,
818
+ bold: true,
819
+ color: "primary",
820
+ spacing: { before: 24, after: 12 },
821
+ keepNext: true,
822
+ alignment: "left",
823
+ borders: {
824
+ bottom: {
825
+ style: "single",
826
+ size: 1,
827
+ color: "accent",
828
+ space: 6
829
+ }
830
+ }
831
+ },
832
+ heading2: {
833
+ font: "heading",
834
+ size: 18,
835
+ bold: true,
836
+ color: "secondary",
837
+ spacing: { before: 18, after: 8 },
838
+ keepNext: true,
839
+ alignment: "left"
840
+ },
841
+ heading3: {
842
+ font: "body",
843
+ size: 13,
844
+ bold: true,
845
+ color: "accent",
846
+ spacing: { before: 12, after: 6 },
847
+ keepNext: true,
848
+ alignment: "left"
849
+ },
850
+ heading4: {
851
+ font: "body",
852
+ size: 12,
853
+ bold: true,
854
+ color: "textSecondary",
855
+ spacing: { before: 8, after: 4 },
856
+ keepNext: true
857
+ },
858
+ title: {
859
+ font: "heading",
860
+ size: 32,
861
+ bold: true,
862
+ color: "primary",
863
+ alignment: "left",
864
+ spacing: { before: 0, after: 12 }
865
+ },
866
+ subtitle: {
867
+ font: "light",
868
+ size: 16,
869
+ italic: false,
870
+ color: "textSecondary",
871
+ alignment: "left",
872
+ spacing: { before: 0, after: 24 }
873
+ }
874
+ },
875
+ componentDefaults: {
876
+ table: {
877
+ borderColor: "#E2E8F0"
878
+ },
879
+ list: {
880
+ format: "bullet",
881
+ bullet: "\u2013",
882
+ indent: 3
883
+ },
884
+ image: {
885
+ alignment: "center"
886
+ },
887
+ statistic: {
888
+ alignment: "center"
889
+ },
890
+ section: {
891
+ pageBreak: false
892
+ },
893
+ heading: {
894
+ numbering: false
895
+ }
896
+ }
897
+ };
898
+ }
899
+ });
900
+
647
901
  // src/templates/themes/index.ts
648
902
  import fs from "fs";
649
903
  import path from "path";
650
904
  import { fileURLToPath } from "url";
905
+ import {
906
+ isValidThemeConfig
907
+ } from "@json-to-office/shared-docx";
651
908
  function loadThemesFromJson() {
652
909
  if (_themesCache) {
653
910
  return _themesCache;
@@ -655,7 +912,9 @@ function loadThemesFromJson() {
655
912
  const themes2 = {
656
913
  minimal: ensureThemeDefaults(minimal_docx_theme_default),
657
914
  corporate: ensureThemeDefaults(corporate_docx_theme_default),
658
- modern: ensureThemeDefaults(modern_docx_theme_default)
915
+ modern: ensureThemeDefaults(modern_docx_theme_default),
916
+ apex: ensureThemeDefaults(apex_docx_theme_default),
917
+ devportal: ensureThemeDefaults(devportal_docx_theme_default)
659
918
  };
660
919
  try {
661
920
  const __filename = fileURLToPath(import.meta.url);
@@ -666,9 +925,18 @@ function loadThemesFromJson() {
666
925
  for (const themeName of themeFiles) {
667
926
  if (!themes2[themeName]) {
668
927
  try {
669
- const filePath = path.join(themesDir, `${themeName}.docx.theme.json`);
928
+ const filePath = path.join(
929
+ themesDir,
930
+ `${themeName}.docx.theme.json`
931
+ );
670
932
  const content = fs.readFileSync(filePath, "utf8");
671
933
  const parsedTheme = JSON.parse(content);
934
+ if (!isValidThemeConfig(parsedTheme)) {
935
+ console.warn(
936
+ `Skipping theme ${themeName}: does not match the theme schema.`
937
+ );
938
+ continue;
939
+ }
672
940
  themes2[themeName] = ensureThemeDefaults(parsedTheme);
673
941
  } catch (error) {
674
942
  console.warn(
@@ -684,7 +952,7 @@ function loadThemesFromJson() {
684
952
  _themesCache = themes2;
685
953
  return themes2;
686
954
  }
687
- var _themesCache, themes, getTheme, getThemeWithFallback, minimalTheme, corporateTheme, modernTheme;
955
+ var _themesCache, themes, getTheme, getThemeWithFallback, hasTheme, getThemeNames, minimalTheme, corporateTheme, modernTheme;
688
956
  var init_themes = __esm({
689
957
  "src/templates/themes/index.ts"() {
690
958
  "use strict";
@@ -692,6 +960,8 @@ var init_themes = __esm({
692
960
  init_minimal_docx_theme();
693
961
  init_corporate_docx_theme();
694
962
  init_modern_docx_theme();
963
+ init_apex_docx_theme();
964
+ init_devportal_docx_theme();
695
965
  _themesCache = null;
696
966
  themes = loadThemesFromJson();
697
967
  getTheme = (themeName) => {
@@ -706,6 +976,12 @@ var init_themes = __esm({
706
976
  }
707
977
  return theme;
708
978
  };
979
+ hasTheme = (themeName) => {
980
+ return themeName in themes;
981
+ };
982
+ getThemeNames = () => {
983
+ return Object.keys(themes);
984
+ };
709
985
  minimalTheme = themes["minimal"];
710
986
  corporateTheme = themes["corporate"];
711
987
  modernTheme = themes["modern"];
@@ -724,10 +1000,13 @@ function resolveColor(colorValue, theme) {
724
1000
  );
725
1001
  }
726
1002
  const colors = getThemeColors(theme);
727
- if (colorValue in colors) {
728
- const resolvedColor = colors[colorValue];
1003
+ const resolvedColor = colors[colorValue];
1004
+ if (typeof resolvedColor === "string") {
729
1005
  return resolveColor(resolvedColor, theme);
730
1006
  }
1007
+ if (/^[0-9A-Fa-f]{6}$/.test(colorValue)) {
1008
+ return colorValue.toUpperCase();
1009
+ }
731
1010
  throw new Error(
732
1011
  `Invalid color value: "${colorValue}". Must be a hex color with # prefix (e.g., "#000000") or a valid theme color name.`
733
1012
  );
@@ -1314,7 +1593,8 @@ var init_loader = __esm({
1314
1593
  // src/themes/json/index.ts
1315
1594
  import {
1316
1595
  ThemeConfigSchema,
1317
- isValidThemeConfig
1596
+ isValidThemeConfig as isValidThemeConfig2,
1597
+ createMinimalTheme
1318
1598
  } from "@json-to-office/shared-docx";
1319
1599
  async function loadThemeFromJson(jsonString) {
1320
1600
  return themeParser.parse(jsonString);
@@ -1338,57 +1618,6 @@ function exportThemeToJson(theme, pretty = true) {
1338
1618
  function validateThemeJsonString(jsonString) {
1339
1619
  return themeParser.validate(jsonString);
1340
1620
  }
1341
- function createMinimalTheme() {
1342
- return {
1343
- name: "minimal-theme",
1344
- displayName: "Minimal Theme",
1345
- description: "A minimal theme with basic styling",
1346
- version: "1.0.0",
1347
- colors: {
1348
- primary: "2563EB",
1349
- secondary: "64748B",
1350
- accent: "F8FAFC",
1351
- text: "334155",
1352
- background: "FFFFFF",
1353
- border: "334155",
1354
- textPrimary: "334155",
1355
- textSecondary: "64748B",
1356
- textMuted: "94A3B8",
1357
- borderPrimary: "334155",
1358
- borderSecondary: "64748B",
1359
- backgroundPrimary: "FFFFFF",
1360
- backgroundSecondary: "F8FAFC"
1361
- },
1362
- fonts: {
1363
- heading: { family: "Arial", size: 14 },
1364
- body: { family: "Arial", size: 11 },
1365
- mono: { family: "Courier New", size: 10 },
1366
- light: { family: "Arial", size: 10 }
1367
- },
1368
- page: {
1369
- size: "A4",
1370
- margins: {
1371
- top: 1440,
1372
- bottom: 1440,
1373
- left: 1440,
1374
- right: 1440,
1375
- header: 720,
1376
- footer: 720,
1377
- gutter: 0
1378
- }
1379
- },
1380
- styles: {
1381
- normal: {
1382
- font: "body",
1383
- size: 11,
1384
- color: "334155",
1385
- alignment: "left",
1386
- lineSpacing: { type: "multiple", value: 1.15 },
1387
- spacing: { after: 8 }
1388
- }
1389
- }
1390
- };
1391
- }
1392
1621
  var themeParser, themeLoader;
1393
1622
  var init_json = __esm({
1394
1623
  "src/themes/json/index.ts"() {
@@ -1712,8 +1941,23 @@ import {
1712
1941
  isVisualComponent
1713
1942
  } from "@json-to-office/shared-docx";
1714
1943
 
1715
- // src/core/generator.ts
1716
- init_styles();
1944
+ // src/styles/theme-resolver.ts
1945
+ init_themes();
1946
+ function resolveBuiltInTheme(themeName, options = {}) {
1947
+ if (!hasTheme(themeName)) {
1948
+ const available = [
1949
+ ...getThemeNames(),
1950
+ ...Object.keys(options.customThemes ?? {})
1951
+ ].sort();
1952
+ options.warnings?.push({
1953
+ component: "theme",
1954
+ message: `Theme "${themeName}" not found; falling back to "minimal". Available themes: ${available.join(", ")}.`,
1955
+ severity: "warning",
1956
+ context: { code: "theme_not_found", requested: themeName, available }
1957
+ });
1958
+ }
1959
+ return getThemeWithFallback(themeName);
1960
+ }
1717
1961
 
1718
1962
  // src/utils/formatters.ts
1719
1963
  import { format } from "date-fns";
@@ -1841,10 +2085,10 @@ var RESOLVER_MAP = {
1841
2085
  visual: resolveVisualProps
1842
2086
  };
1843
2087
  function resolveComponentDefaults(component, theme) {
1844
- if (!component.props) return component;
2088
+ const props = component.props ?? {};
1845
2089
  const resolver = RESOLVER_MAP[component.name];
1846
- const resolvedProps = resolver ? resolver(component.props, theme) : resolveCustomComponentProps(
1847
- component.props,
2090
+ const resolvedProps = resolver ? resolver(props, theme) : resolveCustomComponentProps(
2091
+ props,
1848
2092
  theme,
1849
2093
  component.name
1850
2094
  );
@@ -1908,8 +2152,11 @@ function createDocumentMetadata(props, generationDate = /* @__PURE__ */ new Date
1908
2152
  return {
1909
2153
  title: props.metadata?.title,
1910
2154
  subtitle: props.metadata?.subtitle,
2155
+ description: props.metadata?.description,
1911
2156
  author: props.metadata?.author,
1912
2157
  company: props.metadata?.company,
2158
+ version: props.metadata?.version,
2159
+ tags: props.metadata?.tags,
1913
2160
  date: props.metadata?.date ? new Date(props.metadata.date) : generationDate
1914
2161
  };
1915
2162
  }
@@ -4605,6 +4852,32 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4605
4852
  borderColor: "000000",
4606
4853
  borderSize: 1
4607
4854
  });
4855
+ const warnedCellColors = /* @__PURE__ */ new Set();
4856
+ const warnCellColor = (code, message) => {
4857
+ if (warnedCellColors.has(message)) return;
4858
+ warnedCellColors.add(message);
4859
+ console.warn(`[json-to-docx] ${code}: ${message}`);
4860
+ };
4861
+ const resolveCellColor = (value, prop) => {
4862
+ if (value === void 0 || value === "auto") return value;
4863
+ if (value === "transparent") {
4864
+ if (prop === "backgroundColor") return value;
4865
+ warnCellColor(
4866
+ "TABLE_CELL_COLOR_INVALID",
4867
+ `"transparent" is not a valid table cell "color"; ignoring it and using the table style color. It only applies to "backgroundColor".`
4868
+ );
4869
+ return void 0;
4870
+ }
4871
+ if (/^[0-9A-Fa-f]{6}$/.test(value)) return value.toUpperCase();
4872
+ try {
4873
+ return resolveColor(value, theme);
4874
+ } catch {
4875
+ const allowed = prop === "backgroundColor" ? '"auto", or "transparent" for no shading' : 'or "auto"';
4876
+ throw new Error(
4877
+ `Invalid table cell ${prop}: "${value}". Must be a hex color with # prefix (e.g. "#000000"), a theme color name, ${allowed}.`
4878
+ );
4879
+ }
4880
+ };
4608
4881
  const normalizeBorderColor = (border) => {
4609
4882
  if (border === void 0) return void 0;
4610
4883
  if (typeof border === "string") {
@@ -4741,8 +5014,14 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4741
5014
  tableDef?.padding
4742
5015
  );
4743
5016
  return {
4744
- color: cellDef?.color ?? columnDef?.color ?? tableDef?.color ?? defaults.color,
4745
- backgroundColor: cellDef?.backgroundColor ?? columnDef?.backgroundColor ?? tableDef?.backgroundColor ?? defaults.backgroundColor,
5017
+ color: resolveCellColor(
5018
+ cellDef?.color ?? columnDef?.color ?? tableDef?.color ?? defaults.color,
5019
+ "color"
5020
+ ),
5021
+ backgroundColor: resolveCellColor(
5022
+ cellDef?.backgroundColor ?? columnDef?.backgroundColor ?? tableDef?.backgroundColor ?? defaults.backgroundColor,
5023
+ "backgroundColor"
5024
+ ),
4746
5025
  horizontalAlignment: cellDef?.horizontalAlignment ?? columnDef?.horizontalAlignment ?? tableDef?.horizontalAlignment ?? defaults.horizontalAlignment,
4747
5026
  verticalAlignment: cellDef?.verticalAlignment ?? columnDef?.verticalAlignment ?? tableDef?.verticalAlignment ?? defaults.verticalAlignment,
4748
5027
  font: mergedFont,
@@ -4795,8 +5074,14 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4795
5074
  tableDef?.padding
4796
5075
  );
4797
5076
  return {
4798
- color: headerDef?.color ?? headerTableDef?.color ?? columnDef?.color ?? tableDef?.color ?? defaults.color,
4799
- backgroundColor: headerDef?.backgroundColor ?? headerTableDef?.backgroundColor ?? columnDef?.backgroundColor ?? tableDef?.backgroundColor ?? defaults.backgroundColor,
5077
+ color: resolveCellColor(
5078
+ headerDef?.color ?? headerTableDef?.color ?? columnDef?.color ?? tableDef?.color ?? defaults.color,
5079
+ "color"
5080
+ ),
5081
+ backgroundColor: resolveCellColor(
5082
+ headerDef?.backgroundColor ?? headerTableDef?.backgroundColor ?? columnDef?.backgroundColor ?? tableDef?.backgroundColor ?? defaults.backgroundColor,
5083
+ "backgroundColor"
5084
+ ),
4800
5085
  horizontalAlignment: headerDef?.horizontalAlignment ?? headerTableDef?.horizontalAlignment ?? columnDef?.horizontalAlignment ?? tableDef?.horizontalAlignment ?? defaults.horizontalAlignment,
4801
5086
  verticalAlignment: headerDef?.verticalAlignment ?? headerTableDef?.verticalAlignment ?? columnDef?.verticalAlignment ?? tableDef?.verticalAlignment ?? defaults.verticalAlignment,
4802
5087
  font: mergedFont,
@@ -5018,7 +5303,8 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5018
5303
  void 0
5019
5304
  );
5020
5305
  const headerRow = new TableRow({
5021
- tableHeader: tableConfig.repeatHeaderOnPageBreak,
5306
+ // Headers repeat across page breaks unless explicitly disabled
5307
+ tableHeader: tableConfig.repeatHeaderOnPageBreak ?? true,
5022
5308
  height: headerHeight !== void 0 ? { value: headerHeight * 20, rule: "atLeast" } : void 0,
5023
5309
  children: await Promise.all(
5024
5310
  columns.map(async (column, colIndex) => {
@@ -6349,6 +6635,11 @@ function renderTocComponent(component, theme, context) {
6349
6635
  pageNumbersStart = pageNumbersRange.from;
6350
6636
  pageNumbersEnd = pageNumbersRange.to;
6351
6637
  }
6638
+ if (componentProps.numberingStyle !== void 0) {
6639
+ console.warn(
6640
+ `TOC numberingStyle "${componentProps.numberingStyle}" is ignored: Word's table-of-contents field has no numbering switch. TOC entries inherit numbering from the heading styles they reference.`
6641
+ );
6642
+ }
6352
6643
  const includePageNumbers = componentProps.includePageNumbers !== false;
6353
6644
  const scope = componentProps.scope ?? "auto";
6354
6645
  const effectiveScope = scope === "auto" ? context?.section?.sectionBookmarkId ? "section" : "document" : scope;
@@ -6438,6 +6729,9 @@ function renderTocComponent(component, theme, context) {
6438
6729
  return paragraphs;
6439
6730
  }
6440
6731
 
6732
+ // src/components/highcharts.ts
6733
+ init_colorUtils();
6734
+
6441
6735
  // src/utils/environment.ts
6442
6736
  function isNodeEnvironment() {
6443
6737
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null && typeof process.versions.node === "string";
@@ -6494,6 +6788,7 @@ async function postJsonToService(opts) {
6494
6788
  }
6495
6789
 
6496
6790
  // src/components/highcharts.ts
6791
+ import { DEFAULT_CHART_THEME_COLORS } from "@json-to-office/shared";
6497
6792
  var DEFAULT_EXPORT_SERVER_URL = "http://localhost:7801";
6498
6793
  async function generateChart(config, servicesConfig) {
6499
6794
  if (!isNodeEnvironment()) {
@@ -6534,14 +6829,24 @@ Cause: ${cause}`
6534
6829
  height
6535
6830
  };
6536
6831
  }
6832
+ function toChartColor(value, theme) {
6833
+ if (/^#?[0-9A-Fa-f]{6}$/.test(value)) {
6834
+ return value.startsWith("#") ? value : `#${value}`;
6835
+ }
6836
+ try {
6837
+ return `#${resolveColor(value, theme)}`;
6838
+ } catch {
6839
+ return void 0;
6840
+ }
6841
+ }
6537
6842
  function withThemeColors(config, theme) {
6538
6843
  const options = config.options;
6539
6844
  if (!options || options.colors || !theme?.colors) return config;
6540
- const palette = [
6541
- theme.colors.primary,
6542
- theme.colors.secondary,
6543
- theme.colors.accent
6544
- ].filter((c) => typeof c === "string" && c.length > 0).map((c) => c.startsWith("#") ? c : `#${c}`);
6845
+ const themeColors = theme.colors;
6846
+ const palette = DEFAULT_CHART_THEME_COLORS.map((token) => {
6847
+ const value = themeColors[token];
6848
+ return typeof value === "string" && value.length > 0 ? toChartColor(value, theme) : void 0;
6849
+ }).filter((c) => c !== void 0);
6545
6850
  if (palette.length === 0) return config;
6546
6851
  return {
6547
6852
  ...config,
@@ -6746,6 +7051,22 @@ function getAlignment3(alignment) {
6746
7051
  return AlignmentType5.LEFT;
6747
7052
  }
6748
7053
  }
7054
+ function coreProperties(metadata) {
7055
+ const { title, subtitle, description, author, company, version, tags } = metadata;
7056
+ const customProperties = [
7057
+ ...company ? [{ name: "Company", value: company }] : [],
7058
+ ...version ? [{ name: "Version", value: version }] : []
7059
+ ];
7060
+ return {
7061
+ ...title && { title },
7062
+ ...subtitle && { subject: subtitle },
7063
+ ...description && { description },
7064
+ // Word surfaces both names; author is the only person the document knows.
7065
+ ...author && { creator: author, lastModifiedBy: author },
7066
+ ...tags && tags.length > 0 && { keywords: tags.join(", ") },
7067
+ ...customProperties.length > 0 && { customProperties }
7068
+ };
7069
+ }
6749
7070
  async function renderDocument(structure, layout, options) {
6750
7071
  return runWithGenerationDate(
6751
7072
  structure.metadata.date,
@@ -6834,6 +7155,7 @@ async function renderDocumentScoped(structure, layout, options) {
6834
7155
  return new Document({
6835
7156
  styles: createWordStyles(structure.theme, structure.language),
6836
7157
  sections,
7158
+ ...coreProperties(structure.metadata),
6837
7159
  features: {
6838
7160
  updateFields: true,
6839
7161
  // Required for TOC fields to update correctly
@@ -7232,13 +7554,13 @@ function toDosTime(date) {
7232
7554
  function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
7233
7555
  const zip = new AdmZip2(buffer);
7234
7556
  const isoTimestamp = generatedAt.toISOString();
7235
- const coreProperties = zip.getEntry("docProps/core.xml");
7236
- if (coreProperties) {
7237
- const normalized = coreProperties.getData().toString("utf8").replace(
7557
+ const coreProperties2 = zip.getEntry("docProps/core.xml");
7558
+ if (coreProperties2) {
7559
+ const normalized = coreProperties2.getData().toString("utf8").replace(
7238
7560
  /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g,
7239
7561
  `$1${isoTimestamp}$2`
7240
7562
  );
7241
- zip.updateFile(coreProperties, Buffer.from(normalized, "utf8"));
7563
+ zip.updateFile(coreProperties2, Buffer.from(normalized, "utf8"));
7242
7564
  }
7243
7565
  const zipTimestamp = toDosTime(generatedAt);
7244
7566
  for (const entry of zip.getEntries()) {
@@ -7412,7 +7734,7 @@ function isReportComponentDefinition(definition) {
7412
7734
  return false;
7413
7735
  }
7414
7736
  const def = definition;
7415
- return def.name === "docx" && "props" in def;
7737
+ return def.name === "docx";
7416
7738
  }
7417
7739
  async function generateDocument(document, options) {
7418
7740
  if (!document || document.name !== "docx") {
@@ -7439,7 +7761,7 @@ async function generateFromConfig(props, components, options) {
7439
7761
  return await generateDocument(reportComponent, options);
7440
7762
  }
7441
7763
  async function generateDocumentWithCustomThemes(documentIn, customThemes, services, fonts, warnings, generationDate) {
7442
- let document = documentIn;
7764
+ let document = documentIn.props === void 0 ? { ...documentIn, props: {} } : documentIn;
7443
7765
  let themeName = document.props.theme || "minimal";
7444
7766
  let theme;
7445
7767
  if (customThemes) {
@@ -7453,11 +7775,11 @@ async function generateDocumentWithCustomThemes(documentIn, customThemes, servic
7453
7775
  if (matchingThemeKey) {
7454
7776
  theme = customThemes[matchingThemeKey];
7455
7777
  } else {
7456
- theme = getThemeWithFallback(themeName);
7778
+ theme = resolveBuiltInTheme(themeName, { customThemes, warnings });
7457
7779
  }
7458
7780
  }
7459
7781
  } else {
7460
- theme = getThemeWithFallback(themeName);
7782
+ theme = resolveBuiltInTheme(themeName, { warnings });
7461
7783
  }
7462
7784
  const mode = applyExportMode({ doc: document, theme, fonts });
7463
7785
  document = mode.doc;
@@ -7891,7 +8213,6 @@ async function runExample(example, options = {}) {
7891
8213
  }
7892
8214
 
7893
8215
  // src/plugin/createDocumentGenerator.ts
7894
- init_styles();
7895
8216
  import { applyExportMode as applyExportMode2, scopedThemeName as scopedThemeName2 } from "@json-to-office/shared";
7896
8217
 
7897
8218
  // src/plugin/version-resolver.ts
@@ -8136,7 +8457,7 @@ function mergeSchemas(customComponents, standardSchemas) {
8136
8457
  // src/plugin/createDocumentGenerator.ts
8137
8458
  function createBuilderImpl(state) {
8138
8459
  const componentMap = new Map(state.components.map((c) => [c.name, c]));
8139
- function resolveDocumentTheme(themeName) {
8460
+ function resolveDocumentTheme(themeName, warnings) {
8140
8461
  if (state.customThemes) {
8141
8462
  if (state.customThemes[themeName]) {
8142
8463
  return state.customThemes[themeName];
@@ -8151,7 +8472,10 @@ function createBuilderImpl(state) {
8151
8472
  if (state.theme) {
8152
8473
  return state.theme;
8153
8474
  }
8154
- return getThemeWithFallback(themeName);
8475
+ return resolveBuiltInTheme(themeName, {
8476
+ customThemes: state.customThemes,
8477
+ warnings
8478
+ });
8155
8479
  }
8156
8480
  async function processDocumentComponents(components, preserveSet, warningsCollector, resolvedTheme, validateEmitted, depth = 0) {
8157
8481
  if (depth > 20) {
@@ -8315,7 +8639,8 @@ function createBuilderImpl(state) {
8315
8639
  async function generate(document, options) {
8316
8640
  try {
8317
8641
  const preserveSet = resolvePreserveSet(options);
8318
- const internalDocument = document;
8642
+ const rootIn = document;
8643
+ const internalDocument = rootIn.props === void 0 ? { ...rootIn, props: {} } : rootIn;
8319
8644
  const vOpts = {
8320
8645
  ...state.validation,
8321
8646
  ...options?.validation
@@ -8352,9 +8677,9 @@ function createBuilderImpl(state) {
8352
8677
  );
8353
8678
  }
8354
8679
  };
8355
- const baseThemeName = internalDocument.props.theme || "minimal";
8356
- const docTheme = resolveDocumentTheme(baseThemeName);
8357
8680
  const warnings = [];
8681
+ const baseThemeName = internalDocument.props.theme || "minimal";
8682
+ const docTheme = resolveDocumentTheme(baseThemeName, warnings);
8358
8683
  const mode = applyExportMode2({
8359
8684
  doc: internalDocument,
8360
8685
  theme: docTheme,