@gmb/bitmark-parser-generator 5.25.0 → 5.27.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.
package/dist/cli/main.js CHANGED
@@ -545,6 +545,14 @@ var BitType = {
545
545
  smartStandardRemarkNonNormativeCollapsible: "smart-standard-remark-non-normative-collapsible",
546
546
  smartStandardRemarkNormative: "smart-standard-remark-normative",
547
547
  smartStandardRemarkNormativeCollapsible: "smart-standard-remark-normative-collapsible",
548
+ smartStandardRemarkTableExtendedImageNonNormative: "smart-standard-remark-table-extended-image-non-normative",
549
+ smartStandardRemarkTableExtendedImageNonNormativeCollapsible: "smart-standard-remark-table-extended-image-non-normative-collapsible",
550
+ smartStandardRemarkTableExtendedImageNormative: "smart-standard-remark-table-extended-image-normative",
551
+ smartStandardRemarkTableExtendedImageNormativeCollapsible: "smart-standard-remark-table-extended-image-normative-collapsible",
552
+ smartStandardRemarkTableExtendedNonNormative: "smart-standard-remark-table-extended-non-normative",
553
+ smartStandardRemarkTableExtendedNonNormativeCollapsible: "smart-standard-remark-table-extended-non-normative-collapsible",
554
+ smartStandardRemarkTableExtendedNormative: "smart-standard-remark-table-extended-normative",
555
+ smartStandardRemarkTableExtendedNormativeCollapsible: "smart-standard-remark-table-extended-normative-collapsible",
548
556
  smartStandardRemarkTableImageNonNormative: "smart-standard-remark-table-image-non-normative",
549
557
  smartStandardRemarkTableImageNonNormativeCollapsible: "smart-standard-remark-table-image-non-normative-collapsible",
550
558
  smartStandardRemarkTableImageNormative: "smart-standard-remark-table-image-normative",
@@ -553,6 +561,14 @@ var BitType = {
553
561
  smartStandardRemarkTableNonNormativeCollapsible: "smart-standard-remark-table-non-normative-collapsible",
554
562
  smartStandardRemarkTableNormative: "smart-standard-remark-table-normative",
555
563
  smartStandardRemarkTableNormativeCollapsible: "smart-standard-remark-table-normative-collapsible",
564
+ smartStandardTableExtendedImageNonNormative: "smart-standard-table-extended-image-non-normative",
565
+ smartStandardTableExtendedImageNonNormativeCollapsible: "smart-standard-table-extended-image-non-normative-collapsible",
566
+ smartStandardTableExtendedImageNormative: "smart-standard-table-extended-image-normative",
567
+ smartStandardTableExtendedImageNormativeCollapsible: "smart-standard-table-extended-image-normative-collapsible",
568
+ smartStandardTableExtendedNonNormative: "smart-standard-table-extended-non-normative",
569
+ smartStandardTableExtendedNonNormativeCollapsible: "smart-standard-table-extended-non-normative-collapsible",
570
+ smartStandardTableExtendedNormative: "smart-standard-table-extended-normative",
571
+ smartStandardTableExtendedNormativeCollapsible: "smart-standard-table-extended-normative-collapsible",
556
572
  smartStandardTableImageNonNormative: "smart-standard-table-image-non-normative",
557
573
  smartStandardTableImageNonNormativeCollapsible: "smart-standard-table-image-non-normative-collapsible",
558
574
  smartStandardTableImageNormative: "smart-standard-table-image-normative",
@@ -575,10 +591,18 @@ var BitType = {
575
591
  standardNoteNormative: "standard-note-normative",
576
592
  standardRemarkNonNormative: "standard-remark-non-normative",
577
593
  standardRemarkNormative: "standard-remark-normative",
594
+ standardRemarkTableExtendedImageNonNormative: "standard-remark-table-extended-image-non-normative",
595
+ standardRemarkTableExtendedImageNormative: "standard-remark-table-extended-image-normative",
596
+ standardRemarkTableExtendedNonNormative: "standard-remark-table-extended-non-normative",
597
+ standardRemarkTableExtendedNormative: "standard-remark-table-extended-normative",
578
598
  standardRemarkTableImageNonNormative: "standard-remark-table-image-non-normative",
579
599
  standardRemarkTableImageNormative: "standard-remark-table-image-normative",
580
600
  standardRemarkTableNonNormative: "standard-remark-table-non-normative",
581
601
  standardRemarkTableNormative: "standard-remark-table-normative",
602
+ standardTableExtendedImageNonNormative: "standard-table-extended-image-non-normative",
603
+ standardTableExtendedImageNormative: "standard-table-extended-image-normative",
604
+ standardTableExtendedNonNormative: "standard-table-extended-non-normative",
605
+ standardTableExtendedNormative: "standard-table-extended-normative",
582
606
  standardTableImageNonNormative: "standard-table-image-non-normative",
583
607
  standardTableImageNormative: "standard-table-image-normative",
584
608
  standardTableNonNormative: "standard-table-non-normative",
@@ -777,6 +801,10 @@ var AbstractTagConfig = class {
777
801
  // New JSON-pattern jsonKey (export-only)
778
802
  hasExportJsonKey;
779
803
  // True iff exportJsonKey was explicitly set
804
+ htmlKey;
805
+ // HTML key-pattern (export-only)
806
+ hasHtmlKey;
807
+ // True iff htmlKey was explicitly set
780
808
  deprecated;
781
809
  // Deprecated version
782
810
  constructor(params) {
@@ -789,6 +817,8 @@ var AbstractTagConfig = class {
789
817
  this.jsonKey = params.jsonKey;
790
818
  this.exportJsonKey = params.exportJsonKey;
791
819
  this.hasExportJsonKey = params.hasExportJsonKey;
820
+ this.htmlKey = params.htmlKey;
821
+ this.hasHtmlKey = params.hasHtmlKey;
792
822
  this.deprecated = params.deprecated;
793
823
  }
794
824
  };
@@ -922,9 +952,12 @@ var CardVariantConfig = class {
922
952
  exportJsonKey;
923
953
  // New JSON-pattern jsonKey
924
954
  hasExportJsonKey;
955
+ htmlKey;
956
+ // HTML key-pattern
957
+ hasHtmlKey;
925
958
  format;
926
959
  // Body text format. Default: bitmark++
927
- constructor(tags2, bodyAllowed, bodyRequired, repeatCount, jsonKey, exportJsonKey, hasExportJsonKey = false, format) {
960
+ constructor(tags2, bodyAllowed, bodyRequired, repeatCount, jsonKey, exportJsonKey, hasExportJsonKey = false, format, htmlKey, hasHtmlKey = false) {
928
961
  this.tags = tags2;
929
962
  this.bodyAllowed = bodyAllowed == null ? true : bodyAllowed;
930
963
  this.bodyRequired = bodyRequired;
@@ -932,6 +965,8 @@ var CardVariantConfig = class {
932
965
  this.jsonKey = jsonKey;
933
966
  this.exportJsonKey = exportJsonKey;
934
967
  this.hasExportJsonKey = hasExportJsonKey;
968
+ this.htmlKey = htmlKey;
969
+ this.hasHtmlKey = hasHtmlKey;
935
970
  this.format = format;
936
971
  }
937
972
  toString(options) {
@@ -961,13 +996,17 @@ var CardSideConfig = class {
961
996
  jsonKey;
962
997
  exportJsonKey;
963
998
  hasExportJsonKey;
999
+ htmlKey;
1000
+ hasHtmlKey;
964
1001
  variants;
965
- constructor(name, repeat, variants, jsonKey, exportJsonKey, hasExportJsonKey = false) {
1002
+ constructor(name, repeat, variants, jsonKey, exportJsonKey, hasExportJsonKey = false, htmlKey, hasHtmlKey = false) {
966
1003
  this.name = name;
967
1004
  this.repeat = repeat;
968
1005
  this.jsonKey = jsonKey;
969
1006
  this.exportJsonKey = exportJsonKey;
970
1007
  this.hasExportJsonKey = hasExportJsonKey;
1008
+ this.htmlKey = htmlKey;
1009
+ this.hasHtmlKey = hasHtmlKey;
971
1010
  this.variants = variants;
972
1011
  }
973
1012
  toString(options) {
@@ -993,6 +1032,8 @@ var CardSetConfig = class {
993
1032
  jsonKey;
994
1033
  exportJsonKey;
995
1034
  hasExportJsonKey;
1035
+ htmlKey;
1036
+ hasHtmlKey;
996
1037
  sections;
997
1038
  sides;
998
1039
  // Legacy accessor — provides the same shape as the old `variants: CardVariantConfig[][]`
@@ -1000,11 +1041,13 @@ var CardSetConfig = class {
1000
1041
  get variants() {
1001
1042
  return this.sides.map((side) => side.variants);
1002
1043
  }
1003
- constructor(configKey, jsonKey, exportJsonKey, hasExportJsonKey, sections, sides) {
1044
+ constructor(configKey, jsonKey, exportJsonKey, hasExportJsonKey, sections, sides, htmlKey = void 0, hasHtmlKey = false) {
1004
1045
  this.configKey = configKey;
1005
1046
  this.jsonKey = jsonKey;
1006
1047
  this.exportJsonKey = exportJsonKey;
1007
1048
  this.hasExportJsonKey = hasExportJsonKey;
1049
+ this.htmlKey = htmlKey;
1050
+ this.hasHtmlKey = hasHtmlKey;
1008
1051
  this.sections = sections;
1009
1052
  this.sides = sides;
1010
1053
  }
@@ -3661,6 +3704,16 @@ var CARDSETS = {
3661
3704
  [CardSetConfigKey.table]: {
3662
3705
  jsonKey: null,
3663
3706
  exportJsonKey: null,
3707
+ // PLAN-109 / HTML.md §8 — idiomatic HTML mapping. The classic (non-extended)
3708
+ // table produces the same `<table>`/`<thead>`/`<tbody>`/`<tfoot>` structure as
3709
+ // table-extended, so the structural htmlKeys mirror that cardSet. NOTE: the
3710
+ // JSON side routes the FIRST header card → `columns` (header) and SUBSEQUENT
3711
+ // header cards → `data` (body); HTML uses a uniform `<thead>` for the whole
3712
+ // `table-header` section, so a multi-card header (e.g. a second header card
3713
+ // that JSON sends to `data`) renders as additional `<thead>` rows rather than
3714
+ // moving to `<tbody>`. Per-card header/body splitting needs engine-level
3715
+ // routing not yet expressible in the pattern vocabulary — see PLAN-109.
3716
+ htmlKey: { "@el": "table", "@children": "$" },
3664
3717
  sections: {
3665
3718
  "table-header": {
3666
3719
  // First header card's row of cells → `columns`. Subsequent header
@@ -3679,25 +3732,36 @@ var CARDSETS = {
3679
3732
  },
3680
3733
  { "@cardIndex=0": { "@bit": { table: { columns: "$" } } } },
3681
3734
  { "@bit": { table: { data: ["$"] } } }
3682
- ]
3735
+ ],
3736
+ // Header section → `<thead>` rows of `<th>`; `@tableColWidth` bubbles a
3737
+ // `width` attr onto the enclosing `<th>` (HTML-C2).
3738
+ htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
3739
+ sideHtmlKey: { "@el": "th", "@children": "$" }
3683
3740
  },
3684
3741
  "table-body": {
3685
3742
  // Batched emission: `$` is the entire 2D array of body card rows.
3686
3743
  // This is the default section (no qualifier → cards land here).
3687
3744
  jsonKey: "table.data",
3688
3745
  exportJsonKey: { "@bit": { table: { data: "$" } } },
3746
+ htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
3689
3747
  isDefault: true
3690
3748
  },
3691
3749
  "table-footer": {
3692
3750
  // Batched emission: `$` is the entire 2D array of footer rows.
3693
3751
  jsonKey: "table.data",
3694
- exportJsonKey: { "@bit": { table: { data: "$" } } }
3752
+ exportJsonKey: { "@bit": { table: { data: "$" } } },
3753
+ // Footer cells route to `data` (not `columns`), so they are `<td>` (no
3754
+ // sideHtmlKey override — the cell side default applies).
3755
+ htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } }
3695
3756
  }
3696
3757
  },
3697
3758
  sides: [
3698
3759
  {
3699
3760
  name: "cell",
3700
3761
  repeat: true,
3762
+ // Default (body/footer) cell → `<td>`; the header section overrides to
3763
+ // `<th>` via its sideHtmlKey (HTML.md §8).
3764
+ htmlKey: { "@el": "td", "@children": "$" },
3701
3765
  variants: [
3702
3766
  {
3703
3767
  jsonKey: null,
@@ -3714,6 +3778,8 @@ var CARDSETS = {
3714
3778
  },
3715
3779
  {
3716
3780
  key: ConfigKey.property_tableCellType,
3781
+ // HTML.md §8: first-match predicate selecting `<th>` vs `<td>`.
3782
+ htmlKey: [{ "@value=th": { "@el": "th" } }, { "@absent": { "@el": "td" } }],
3717
3783
  description: "Table cell type (th/td).",
3718
3784
  format: TagFormat.plainText
3719
3785
  },
@@ -3736,6 +3802,9 @@ var CARDSETS = {
3736
3802
  key: ConfigKey.property_tableColWidth,
3737
3803
  jsonKey: "^table.columnWidths[{s}]",
3738
3804
  exportJsonKey: { "@bit": { table: { columnWidths: { $s: "$" } } } },
3805
+ // HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
3806
+ // enclosing cell element (`<th>`/`<td>`).
3807
+ htmlKey: { "@el": { "@attr": { width: "$" } } },
3739
3808
  description: "Width for the column.",
3740
3809
  format: TagFormat.number
3741
3810
  }
@@ -3751,6 +3820,12 @@ var CARDSETS = {
3751
3820
  //
3752
3821
  [CardSetConfigKey.tableExtended]: {
3753
3822
  jsonKey: null,
3823
+ // PLAN-109 / HTML.md §8 — idiomatic HTML mapping for the extended table.
3824
+ // The card-set container is the `<table>` element; each section is a
3825
+ // table section element (`<thead>`/`<tbody>`/`<tfoot>`) whose children are
3826
+ // `<tr>` rows, and each cell side is a `<td>` (body) or `<th>` (header /
3827
+ // footer, via the section sideHtmlKey override).
3828
+ htmlKey: { "@el": "table", "@children": "$" },
3754
3829
  sections: {
3755
3830
  "table-header": {
3756
3831
  jsonKey: "table.header.rows",
@@ -3770,19 +3845,29 @@ var CARDSETS = {
3770
3845
  },
3771
3846
  { table: { header: { rows: ["$"] } } }
3772
3847
  ],
3848
+ htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
3773
3849
  sideJsonKey: "cells[{s}]|set(title=true)",
3774
- sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } }
3850
+ sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
3851
+ // Header cells are `<th>`. The `width` attribute is contributed by the
3852
+ // `@tableColWidth` tag, which bubbles it onto the enclosing `<th>`
3853
+ // (HTML-C2) — so it is not repeated here.
3854
+ sideHtmlKey: { "@el": "th", "@children": "$" }
3775
3855
  },
3776
3856
  "table-body": {
3777
3857
  jsonKey: "table.body.rows",
3778
3858
  exportJsonKey: { table: { body: { rows: "$" } } },
3859
+ htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
3779
3860
  isDefault: true
3780
3861
  },
3781
3862
  "table-footer": {
3782
3863
  jsonKey: "table.footer.rows",
3783
3864
  exportJsonKey: { table: { footer: { rows: "$" } } },
3865
+ htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } },
3784
3866
  sideJsonKey: "cells[{s}]|set(title=true)",
3785
- sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } }
3867
+ sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
3868
+ // Footer cells render as `<th>` (mirrors the header `title: true`
3869
+ // mapping); §8 lists only the header side, so this parallels it.
3870
+ sideHtmlKey: { "@el": "th", "@children": "$" }
3786
3871
  }
3787
3872
  },
3788
3873
  sides: [
@@ -3791,6 +3876,9 @@ var CARDSETS = {
3791
3876
  repeat: true,
3792
3877
  jsonKey: "cells[{s}]",
3793
3878
  exportJsonKey: { cells: { $s: "$" } },
3879
+ // Default (body) cell → `<td>`; the header/footer sections override the
3880
+ // side to `<th>` via their sideHtmlKey (HTML.md §8).
3881
+ htmlKey: { "@el": "td", "@children": "$" },
3794
3882
  variants: [
3795
3883
  {
3796
3884
  jsonKey: "content",
@@ -3814,6 +3902,9 @@ var CARDSETS = {
3814
3902
  key: ConfigKey.property_tableCellType,
3815
3903
  jsonKey: "title|bool(th)",
3816
3904
  exportJsonKey: [{ "@value=th": { title: true } }],
3905
+ // HTML.md §8: same first-match predicate machinery selecting
3906
+ // `<th>` vs `<td>` instead of `title:true` vs nothing.
3907
+ htmlKey: [{ "@value=th": { "@el": "th" } }, { "@absent": { "@el": "td" } }],
3817
3908
  description: "Table cell type (th/td).",
3818
3909
  format: TagFormat.plainText
3819
3910
  },
@@ -3843,6 +3934,9 @@ var CARDSETS = {
3843
3934
  // PLAN-080: write to the cell's `colwidth` field.
3844
3935
  jsonKey: "colwidth",
3845
3936
  exportJsonKey: { colwidth: "$" },
3937
+ // HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
3938
+ // enclosing cell element (`<th>`/`<td>`).
3939
+ htmlKey: { "@el": { "@attr": { width: "$" } } },
3846
3940
  description: "Width for the column.",
3847
3941
  format: TagFormat.number
3848
3942
  }
@@ -6771,7 +6865,9 @@ var ConfigHydrator = class {
6771
6865
  variantsOfSide,
6772
6866
  _side.jsonKey,
6773
6867
  _side.exportJsonKey,
6774
- Object.prototype.hasOwnProperty.call(_side, "exportJsonKey")
6868
+ Object.prototype.hasOwnProperty.call(_side, "exportJsonKey"),
6869
+ _side.htmlKey,
6870
+ Object.prototype.hasOwnProperty.call(_side, "htmlKey")
6775
6871
  );
6776
6872
  sides.push(sideConfig);
6777
6873
  }
@@ -6781,7 +6877,9 @@ var ConfigHydrator = class {
6781
6877
  _cardSetConfig.exportJsonKey,
6782
6878
  Object.prototype.hasOwnProperty.call(_cardSetConfig, "exportJsonKey"),
6783
6879
  this.hydrateCardSections(_cardSetConfig.sections),
6784
- sides
6880
+ sides,
6881
+ _cardSetConfig.htmlKey,
6882
+ Object.prototype.hasOwnProperty.call(_cardSetConfig, "htmlKey")
6785
6883
  );
6786
6884
  return cardSetConfig;
6787
6885
  }
@@ -6793,10 +6891,14 @@ var ConfigHydrator = class {
6793
6891
  jsonKey: v.jsonKey,
6794
6892
  exportJsonKey: v.exportJsonKey,
6795
6893
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(v, "exportJsonKey"),
6894
+ htmlKey: v.htmlKey,
6895
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(v, "htmlKey"),
6796
6896
  isDefault: v.isDefault,
6797
6897
  sideJsonKey: v.sideJsonKey,
6798
6898
  sideExportJsonKey: v.sideExportJsonKey,
6799
6899
  hasSideExportJsonKey: Object.prototype.hasOwnProperty.call(v, "sideExportJsonKey"),
6900
+ sideHtmlKey: v.sideHtmlKey,
6901
+ hasSideHtmlKey: Object.prototype.hasOwnProperty.call(v, "sideHtmlKey"),
6800
6902
  // PLAN-085: cardinality fields pass through; defaults applied
6801
6903
  // downstream by the config consumer (Rust treats `0` / undefined
6802
6904
  // as unbounded).
@@ -6814,7 +6916,8 @@ var ConfigHydrator = class {
6814
6916
  chain: _chain,
6815
6917
  deprecated,
6816
6918
  jsonKey,
6817
- exportJsonKey
6919
+ exportJsonKey,
6920
+ htmlKey
6818
6921
  } = _tag;
6819
6922
  const configKey = Enum(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
6820
6923
  if (!configKey) throw new Error(`No tag key found for config key '${configKey}'`);
@@ -6833,6 +6936,8 @@ var ConfigHydrator = class {
6833
6936
  jsonKey,
6834
6937
  exportJsonKey,
6835
6938
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
6939
+ htmlKey,
6940
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
6836
6941
  deprecated
6837
6942
  });
6838
6943
  return {
@@ -6852,7 +6957,8 @@ var ConfigHydrator = class {
6852
6957
  values,
6853
6958
  defaultValue,
6854
6959
  jsonKey,
6855
- exportJsonKey
6960
+ exportJsonKey,
6961
+ htmlKey
6856
6962
  } = _tag;
6857
6963
  const configKey = Enum(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
6858
6964
  if (!configKey) throw new Error(`No property key found for config key '${configKey}'`);
@@ -6870,6 +6976,8 @@ var ConfigHydrator = class {
6870
6976
  jsonKey,
6871
6977
  exportJsonKey,
6872
6978
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
6979
+ htmlKey,
6980
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
6873
6981
  format,
6874
6982
  values,
6875
6983
  defaultValue,
@@ -6889,7 +6997,8 @@ var ConfigHydrator = class {
6889
6997
  chain: _chain,
6890
6998
  deprecated,
6891
6999
  jsonKey,
6892
- exportJsonKey
7000
+ exportJsonKey,
7001
+ htmlKey
6893
7002
  } = _tag;
6894
7003
  const configKey = Enum(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
6895
7004
  if (!configKey) throw new Error(`No resource key found for config key '${configKey}'`);
@@ -6907,6 +7016,8 @@ var ConfigHydrator = class {
6907
7016
  jsonKey,
6908
7017
  exportJsonKey,
6909
7018
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
7019
+ htmlKey,
7020
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
6910
7021
  deprecated
6911
7022
  });
6912
7023
  return {
@@ -6945,6 +7056,7 @@ var ConfigHydrator = class {
6945
7056
  repeatCount,
6946
7057
  jsonKey,
6947
7058
  exportJsonKey,
7059
+ htmlKey,
6948
7060
  format
6949
7061
  } = _variant;
6950
7062
  const tags2 = this.hydrateTagsConfig(_tags);
@@ -6956,7 +7068,9 @@ var ConfigHydrator = class {
6956
7068
  jsonKey,
6957
7069
  exportJsonKey,
6958
7070
  Object.prototype.hasOwnProperty.call(_variant, "exportJsonKey"),
6959
- format
7071
+ format,
7072
+ htmlKey,
7073
+ Object.prototype.hasOwnProperty.call(_variant, "htmlKey")
6960
7074
  );
6961
7075
  return cardSetConfig;
6962
7076
  }
@@ -10767,6 +10881,26 @@ var BITS = {
10767
10881
  baseBitType: BitType.tableImage,
10768
10882
  description: "Standard non-normative remark table image bit, used to create non-normative remark images in tables in articles or books"
10769
10883
  },
10884
+ [BitType.standardTableExtendedImageNormative]: {
10885
+ since: "1.16.0",
10886
+ baseBitType: BitType.tableExtendedImage,
10887
+ description: "Standard normative extended table image bit, used to create normative extended images in tables in articles or books"
10888
+ },
10889
+ [BitType.standardTableExtendedImageNonNormative]: {
10890
+ since: "1.16.0",
10891
+ baseBitType: BitType.tableExtendedImage,
10892
+ description: "Standard non-normative extended table image bit, used to create non-normative extended images in tables in articles or books"
10893
+ },
10894
+ [BitType.standardRemarkTableExtendedImageNormative]: {
10895
+ since: "1.17.0",
10896
+ baseBitType: BitType.tableExtendedImage,
10897
+ description: "Standard normative remark extended table image bit, used to create normative remark extended images in tables in articles or books"
10898
+ },
10899
+ [BitType.standardRemarkTableExtendedImageNonNormative]: {
10900
+ since: "1.17.0",
10901
+ baseBitType: BitType.tableExtendedImage,
10902
+ description: "Standard non-normative remark extended table image bit, used to create non-normative remark extended images in tables in articles or books"
10903
+ },
10770
10904
  [BitType.smartStandardTableImageNormative]: {
10771
10905
  since: "1.28.0",
10772
10906
  baseBitType: BitType.standardTableImageNormative,
@@ -10787,6 +10921,26 @@ var BITS = {
10787
10921
  baseBitType: BitType.standardRemarkTableImageNonNormative,
10788
10922
  description: "Smart standard non-normative remark table image bit, used to create smart standard non-normative remark images in tables in articles or books"
10789
10923
  },
10924
+ [BitType.smartStandardTableExtendedImageNormative]: {
10925
+ since: "1.28.0",
10926
+ baseBitType: BitType.standardTableExtendedImageNormative,
10927
+ description: "Smart standard normative extended table image bit, used to create smart standard normative extended images in tables in articles or books"
10928
+ },
10929
+ [BitType.smartStandardTableExtendedImageNonNormative]: {
10930
+ since: "1.28.0",
10931
+ baseBitType: BitType.standardTableExtendedImageNonNormative,
10932
+ description: "Smart standard non-normative extended table image bit, used to create smart standard non-normative extended images in tables in articles or books"
10933
+ },
10934
+ [BitType.smartStandardRemarkTableExtendedImageNormative]: {
10935
+ since: "1.28.0",
10936
+ baseBitType: BitType.standardRemarkTableExtendedImageNormative,
10937
+ description: "Smart standard normative remark extended table image bit, used to create smart standard normative remark extended images in tables in articles or books"
10938
+ },
10939
+ [BitType.smartStandardRemarkTableExtendedImageNonNormative]: {
10940
+ since: "1.28.0",
10941
+ baseBitType: BitType.standardRemarkTableExtendedImageNonNormative,
10942
+ description: "Smart standard non-normative remark extended table image bit, used to create smart standard non-normative remark extended images in tables in articles or books"
10943
+ },
10790
10944
  [BitType.smartStandardTableImageNormativeCollapsible]: {
10791
10945
  since: "1.28.0",
10792
10946
  baseBitType: BitType.smartStandardTableImageNormative,
@@ -10807,6 +10961,26 @@ var BITS = {
10807
10961
  baseBitType: BitType.smartStandardRemarkTableImageNonNormative,
10808
10962
  description: "Collapsible smart standard non-normative remark table image bit, used to create collapsible smart standard non-normative remark images in tables in articles or books"
10809
10963
  },
10964
+ [BitType.smartStandardTableExtendedImageNormativeCollapsible]: {
10965
+ since: "1.28.0",
10966
+ baseBitType: BitType.smartStandardTableExtendedImageNormative,
10967
+ description: "Collapsible smart standard normative extended table image bit, used to create collapsible smart standard normative extended images in tables in articles or books"
10968
+ },
10969
+ [BitType.smartStandardTableExtendedImageNonNormativeCollapsible]: {
10970
+ since: "1.28.0",
10971
+ baseBitType: BitType.smartStandardTableExtendedImageNonNormative,
10972
+ description: "Collapsible smart standard non-normative extended table image bit, used to create collapsible smart standard non-normative extended images in tables in articles or books"
10973
+ },
10974
+ [BitType.smartStandardRemarkTableExtendedImageNormativeCollapsible]: {
10975
+ since: "1.28.0",
10976
+ baseBitType: BitType.smartStandardRemarkTableExtendedImageNormative,
10977
+ description: "Collapsible smart standard normative remark extended table image bit, used to create collapsible smart standard normative remark extended images in tables in articles or books"
10978
+ },
10979
+ [BitType.smartStandardRemarkTableExtendedImageNonNormativeCollapsible]: {
10980
+ since: "1.28.0",
10981
+ baseBitType: BitType.smartStandardRemarkTableExtendedImageNonNormative,
10982
+ description: "Collapsible smart standard non-normative remark extended table image bit, used to create collapsible smart standard non-normative remark extended images in tables in articles or books"
10983
+ },
10810
10984
  [BitType.imageLink]: {
10811
10985
  since: "1.3.0",
10812
10986
  baseBitType: BitType._standard,
@@ -12483,6 +12657,26 @@ var BITS = {
12483
12657
  baseBitType: BitType.table,
12484
12658
  description: "Standard non-normative remark table bit, used to create standard non-normative remark tables in articles or books"
12485
12659
  },
12660
+ [BitType.standardTableExtendedNormative]: {
12661
+ since: "1.16.0",
12662
+ baseBitType: BitType.tableExtended,
12663
+ description: "Standard normative extended table bit, used to create standard normative extended tables in articles or books"
12664
+ },
12665
+ [BitType.standardTableExtendedNonNormative]: {
12666
+ since: "1.16.0",
12667
+ baseBitType: BitType.tableExtended,
12668
+ description: "Standard non-normative extended table bit, used to create standard non-normative extended tables in articles or books"
12669
+ },
12670
+ [BitType.standardRemarkTableExtendedNormative]: {
12671
+ since: "1.17.0",
12672
+ baseBitType: BitType.tableExtended,
12673
+ description: "Standard normative remark extended table bit, used to create standard normative remark extended tables in articles or books"
12674
+ },
12675
+ [BitType.standardRemarkTableExtendedNonNormative]: {
12676
+ since: "1.17.0",
12677
+ baseBitType: BitType.tableExtended,
12678
+ description: "Standard non-normative remark extended table bit, used to create standard non-normative remark extended tables in articles or books"
12679
+ },
12486
12680
  [BitType.smartStandardTableNormative]: {
12487
12681
  since: "1.28.0",
12488
12682
  baseBitType: BitType.standardTableNormative,
@@ -12503,6 +12697,26 @@ var BITS = {
12503
12697
  baseBitType: BitType.standardRemarkTableNonNormative,
12504
12698
  description: "Smart standard non-normative remark table bit, used to create smart standard non-normative remark tables in articles or books"
12505
12699
  },
12700
+ [BitType.smartStandardTableExtendedNormative]: {
12701
+ since: "1.28.0",
12702
+ baseBitType: BitType.standardTableExtendedNormative,
12703
+ description: "Smart standard normative extended table bit, used to create smart standard normative extended tables in articles or books"
12704
+ },
12705
+ [BitType.smartStandardTableExtendedNonNormative]: {
12706
+ since: "1.28.0",
12707
+ baseBitType: BitType.standardTableExtendedNonNormative,
12708
+ description: "Smart standard non-normative extended table bit, used to create smart standard non-normative extended tables in articles or books"
12709
+ },
12710
+ [BitType.smartStandardRemarkTableExtendedNormative]: {
12711
+ since: "1.28.0",
12712
+ baseBitType: BitType.standardRemarkTableExtendedNormative,
12713
+ description: "Smart standard normative remark extended table bit, used to create smart standard normative remark extended tables in articles or books"
12714
+ },
12715
+ [BitType.smartStandardRemarkTableExtendedNonNormative]: {
12716
+ since: "1.28.0",
12717
+ baseBitType: BitType.standardRemarkTableExtendedNonNormative,
12718
+ description: "Smart standard non-normative remark extended table bit, used to create smart standard non-normative remark extended tables in articles or books"
12719
+ },
12506
12720
  [BitType.smartStandardTableNormativeCollapsible]: {
12507
12721
  since: "1.28.0",
12508
12722
  baseBitType: BitType.smartStandardTableNormative,
@@ -12523,6 +12737,26 @@ var BITS = {
12523
12737
  baseBitType: BitType.smartStandardRemarkTableNonNormative,
12524
12738
  description: "Collapsible smart standard non-normative remark table bit, used to create collapsible smart standard non-normative remark tables in articles or books"
12525
12739
  },
12740
+ [BitType.smartStandardTableExtendedNormativeCollapsible]: {
12741
+ since: "1.28.0",
12742
+ baseBitType: BitType.smartStandardTableExtendedNormative,
12743
+ description: "Collapsible smart standard normative extended table bit, used to create collapsible smart standard normative extended tables in articles or books"
12744
+ },
12745
+ [BitType.smartStandardTableExtendedNonNormativeCollapsible]: {
12746
+ since: "1.28.0",
12747
+ baseBitType: BitType.smartStandardTableExtendedNonNormative,
12748
+ description: "Collapsible smart standard non-normative extended table bit, used to create collapsible smart standard non-normative extended tables in articles or books"
12749
+ },
12750
+ [BitType.smartStandardRemarkTableExtendedNormativeCollapsible]: {
12751
+ since: "1.28.0",
12752
+ baseBitType: BitType.smartStandardRemarkTableExtendedNormative,
12753
+ description: "Collapsible smart standard normative remark extended table bit, used to create collapsible smart standard normative remark extended tables in articles or books"
12754
+ },
12755
+ [BitType.smartStandardRemarkTableExtendedNonNormativeCollapsible]: {
12756
+ since: "1.28.0",
12757
+ baseBitType: BitType.smartStandardRemarkTableExtendedNonNormative,
12758
+ description: "Collapsible smart standard non-normative remark extended table bit, used to create collapsible smart standard non-normative remark extended tables in articles or books"
12759
+ },
12526
12760
  [BitType.parameters]: {
12527
12761
  since: "1.18.0",
12528
12762
  baseBitType: BitType.table,
@@ -13464,6 +13698,8 @@ var Config = class {
13464
13698
  jsonKey: tag.jsonKey,
13465
13699
  exportJsonKey: tag.exportJsonKey,
13466
13700
  hasExportJsonKey: tag.hasExportJsonKey,
13701
+ htmlKey: tag.htmlKey,
13702
+ hasHtmlKey: tag.hasHtmlKey,
13467
13703
  deprecated: tag.deprecated
13468
13704
  });
13469
13705
  finalResourceTags[k] = newTag;
@@ -13568,7 +13804,7 @@ var instance2 = new Config();
13568
13804
  // src/generated/package_info.ts
13569
13805
  var PACKAGE_INFO = {
13570
13806
  "name": "@gmb/bitmark-parser-generator",
13571
- "version": "5.25.0",
13807
+ "version": "5.27.0",
13572
13808
  "license": "ISC"};
13573
13809
  var Environment = {
13574
13810
  unknown: "",
@@ -32687,6 +32923,233 @@ var BitmarkStringGenerator = class {
32687
32923
  return this.writer.getString();
32688
32924
  }
32689
32925
  };
32926
+
32927
+ // src/generator/html/HtmlTableGenerator.ts
32928
+ var MARK_TO_TAG = {
32929
+ bold: "b",
32930
+ italic: "i",
32931
+ superscript: "sup",
32932
+ subscript: "sub",
32933
+ strike: "s",
32934
+ del: "del",
32935
+ ins: "ins",
32936
+ underline: "u",
32937
+ highlight: "mark",
32938
+ code: "code"
32939
+ };
32940
+ var HtmlTableGenerator = class {
32941
+ indentUnit = " ";
32942
+ /**
32943
+ * Generate HTML table fragments from a string or JSON value.
32944
+ * @returns the concatenated `<table>` fragments, blank-line separated (empty string if none).
32945
+ */
32946
+ generate(input) {
32947
+ let data = input;
32948
+ if (typeof data === "string") {
32949
+ try {
32950
+ data = JSON.parse(data);
32951
+ } catch {
32952
+ return "";
32953
+ }
32954
+ }
32955
+ const bits = this.collectBits(data);
32956
+ const fragments = [];
32957
+ for (const bit of bits) {
32958
+ const html = this.renderTableBit(bit);
32959
+ if (html) fragments.push(html);
32960
+ }
32961
+ return fragments.join("\n\n");
32962
+ }
32963
+ // --- bit collection -------------------------------------------------------
32964
+ collectBits(data) {
32965
+ const out = [];
32966
+ const visit = (value) => {
32967
+ if (value == null || typeof value !== "object") return;
32968
+ if (Array.isArray(value)) {
32969
+ value.forEach(visit);
32970
+ return;
32971
+ }
32972
+ const obj = value;
32973
+ if ("bit" in obj && obj.bit && typeof obj.bit === "object") {
32974
+ out.push(obj.bit);
32975
+ } else if ("type" in obj) {
32976
+ out.push(obj);
32977
+ }
32978
+ };
32979
+ visit(data);
32980
+ return out;
32981
+ }
32982
+ // --- table rendering ------------------------------------------------------
32983
+ renderTableBit(bit) {
32984
+ const table = bit.table;
32985
+ if (!table) return "";
32986
+ if (bit.type !== "table" && bit.type !== "table-extended") return "";
32987
+ const extended = this.toExtended(table);
32988
+ const lines = [];
32989
+ lines.push("<table>");
32990
+ if (bit.caption != null) {
32991
+ const caption = this.cellContentToHtml(this.asTextAst(bit.caption));
32992
+ if (caption) lines.push(`${this.indentUnit}<caption>${caption}</caption>`);
32993
+ }
32994
+ this.renderSection(lines, "thead", extended.header, "header");
32995
+ this.renderSection(lines, "tbody", extended.body, "body");
32996
+ this.renderSection(lines, "tfoot", extended.footer, "footer");
32997
+ lines.push("</table>");
32998
+ return lines.join("\n");
32999
+ }
33000
+ toExtended(table) {
33001
+ const t = table;
33002
+ if (t.header || t.body || t.footer) return table;
33003
+ return convertBasicToExtendedTableFormat(table);
33004
+ }
33005
+ renderSection(lines, tag, section, key) {
33006
+ if (!section || !section.rows || section.rows.length === 0) return;
33007
+ lines.push(`${this.indentUnit}<${tag}>`);
33008
+ for (const row of section.rows) {
33009
+ lines.push(`${this.indentUnit.repeat(2)}<tr>`);
33010
+ for (const cell of row.cells ?? []) {
33011
+ lines.push(`${this.indentUnit.repeat(3)}${this.renderCell(cell, key)}`);
33012
+ }
33013
+ lines.push(`${this.indentUnit.repeat(2)}</tr>`);
33014
+ }
33015
+ lines.push(`${this.indentUnit}</${tag}>`);
33016
+ }
33017
+ renderCell(cell, _key) {
33018
+ const tag = cell.title === true ? "th" : "td";
33019
+ const attrs = [];
33020
+ if (cell.rowspan && cell.rowspan > 1) attrs.push(`rowspan="${cell.rowspan}"`);
33021
+ if (cell.colspan && cell.colspan > 1) attrs.push(`colspan="${cell.colspan}"`);
33022
+ if (cell.scope) attrs.push(`scope="${cell.scope}"`);
33023
+ if (cell.colwidth && cell.colwidth > 0) attrs.push(`width="${cell.colwidth}"`);
33024
+ const open = attrs.length > 0 ? `<${tag} ${attrs.join(" ")}>` : `<${tag}>`;
33025
+ return `${open}${this.cellContentToHtml(this.asTextAst(cell.content))}</${tag}>`;
33026
+ }
33027
+ // --- cell content (TextAst -> HTML) ---------------------------------------
33028
+ asTextAst(content) {
33029
+ if (content == null) return [];
33030
+ if (typeof content === "string") return [{ type: "text", text: content }];
33031
+ return content;
33032
+ }
33033
+ cellContentToHtml(blocks) {
33034
+ if (blocks.length === 0) return "";
33035
+ if (blocks.length === 1 && blocks[0].type === "paragraph") {
33036
+ return this.inlineToHtml(blocks[0].content ?? []);
33037
+ }
33038
+ return blocks.map((b) => this.blockToHtml(b)).join("");
33039
+ }
33040
+ blockToHtml(node) {
33041
+ switch (node.type) {
33042
+ case "paragraph":
33043
+ return `<p>${this.inlineToHtml(node.content ?? [])}</p>`;
33044
+ case "bulletList":
33045
+ case "noBulletList":
33046
+ case "orderedList":
33047
+ case "orderedListRoman":
33048
+ case "orderedListRomanLower":
33049
+ case "letteredList":
33050
+ case "letteredListLower":
33051
+ case "taskList":
33052
+ return this.listToHtml(node);
33053
+ case "image":
33054
+ case "imageInline":
33055
+ return this.imageToHtml(node);
33056
+ default:
33057
+ return this.inlineToHtml([node]);
33058
+ }
33059
+ }
33060
+ inlineToHtml(nodes) {
33061
+ return nodes.map((n) => this.inlineNodeToHtml(n)).join("");
33062
+ }
33063
+ inlineNodeToHtml(node) {
33064
+ if (node.type === "hardBreak") return "<br>";
33065
+ if (node.type === "image" || node.type === "imageInline") return this.imageToHtml(node);
33066
+ if (node.type === "text") {
33067
+ return this.wrapMarks(this.escapeText(node.text ?? ""), node.marks);
33068
+ }
33069
+ if (node.content) return this.inlineToHtml(node.content);
33070
+ return this.escapeText(node.text ?? "");
33071
+ }
33072
+ wrapMarks(text, marks) {
33073
+ if (!marks || marks.length === 0) return text;
33074
+ let open = "";
33075
+ let close = "";
33076
+ for (const mark of marks) {
33077
+ if (mark.type === "link") {
33078
+ const href = mark.attrs?.href ?? "";
33079
+ open += `<a href="${this.escapeAttr(href)}">`;
33080
+ close = `</a>${close}`;
33081
+ } else {
33082
+ const tag = MARK_TO_TAG[mark.type];
33083
+ if (tag) {
33084
+ open += `<${tag}>`;
33085
+ close = `</${tag}>${close}`;
33086
+ }
33087
+ }
33088
+ }
33089
+ return `${open}${text}${close}`;
33090
+ }
33091
+ // --- lists ----------------------------------------------------------------
33092
+ listToHtml(node) {
33093
+ const { tag, attrs } = this.listTag(node);
33094
+ const items = (node.content ?? []).map((item) => this.listItemToHtml(item, node.type === "taskList")).join("");
33095
+ return `<${tag}${attrs}>${items}</${tag}>`;
33096
+ }
33097
+ listTag(node) {
33098
+ const start = node.attrs?.start;
33099
+ const startAttr = typeof start === "number" && start !== 1 ? ` start="${start}"` : "";
33100
+ switch (node.type) {
33101
+ case "orderedList":
33102
+ return { tag: "ol", attrs: startAttr };
33103
+ case "orderedListRoman":
33104
+ return { tag: "ol", attrs: ` type="I"${startAttr}` };
33105
+ case "orderedListRomanLower":
33106
+ return { tag: "ol", attrs: ` type="i"${startAttr}` };
33107
+ case "letteredList":
33108
+ return { tag: "ol", attrs: ` type="A"${startAttr}` };
33109
+ case "letteredListLower":
33110
+ return { tag: "ol", attrs: ` type="a"${startAttr}` };
33111
+ case "noBulletList":
33112
+ return { tag: "ul", attrs: ' style="list-style:none"' };
33113
+ case "taskList":
33114
+ case "bulletList":
33115
+ default:
33116
+ return { tag: "ul", attrs: "" };
33117
+ }
33118
+ }
33119
+ listItemToHtml(item, task) {
33120
+ const inner = (item.content ?? []).map((b) => {
33121
+ if (b.type === "paragraph") return this.inlineToHtml(b.content ?? []);
33122
+ return this.blockToHtml(b);
33123
+ }).join("");
33124
+ if (task) {
33125
+ const checked = item.attrs?.checked ? " checked" : "";
33126
+ return `<li><input type="checkbox"${checked}>${inner}</li>`;
33127
+ }
33128
+ return `<li>${inner}</li>`;
33129
+ }
33130
+ // --- images ---------------------------------------------------------------
33131
+ imageToHtml(node) {
33132
+ const attrs = node.attrs ?? {};
33133
+ const parts = [];
33134
+ const src = attrs.src;
33135
+ parts.push(`src="${this.escapeAttr(typeof src === "string" ? src : "")}"`);
33136
+ if (typeof attrs.alt === "string") parts.push(`alt="${this.escapeAttr(attrs.alt)}"`);
33137
+ if (typeof attrs.title === "string") parts.push(`title="${this.escapeAttr(attrs.title)}"`);
33138
+ if (attrs.width != null) parts.push(`width="${attrs.width}"`);
33139
+ if (attrs.height != null) parts.push(`height="${attrs.height}"`);
33140
+ if (typeof attrs.class === "string" && attrs.class) {
33141
+ parts.push(`class="${this.escapeAttr(attrs.class)}"`);
33142
+ }
33143
+ return `<img ${parts.join(" ")}>`;
33144
+ }
33145
+ // --- escaping -------------------------------------------------------------
33146
+ escapeText(text) {
33147
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
33148
+ }
33149
+ escapeAttr(text) {
33150
+ return this.escapeText(text).replace(/"/g, "&quot;");
33151
+ }
33152
+ };
32690
33153
  var MOVE_BODY_RECURSION_LIMIT = 5e3;
32691
33154
  var DEFAULT_OPTIONS3 = {
32692
33155
  // debugGenerationInline: true,
@@ -37533,6 +37996,8 @@ function propertyContentProcessor(context, contentDepth, tagsConfig, content, ta
37533
37996
  jsonKey: void 0,
37534
37997
  exportJsonKey: void 0,
37535
37998
  hasExportJsonKey: false,
37999
+ htmlKey: void 0,
38000
+ hasHtmlKey: false,
37536
38001
  format: TagFormat.bitmarkText,
37537
38002
  values: void 0,
37538
38003
  defaultValue: void 0,
@@ -41887,6 +42352,497 @@ var BitmarkParser = class {
41887
42352
  return parse2(bitmark);
41888
42353
  }
41889
42354
  };
42355
+
42356
+ // src/parser/html/HtmlDom.ts
42357
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
42358
+ "area",
42359
+ "base",
42360
+ "br",
42361
+ "col",
42362
+ "embed",
42363
+ "hr",
42364
+ "img",
42365
+ "input",
42366
+ "link",
42367
+ "meta",
42368
+ "param",
42369
+ "source",
42370
+ "track",
42371
+ "wbr"
42372
+ ]);
42373
+ var IMPLIED_CLOSE = {
42374
+ li: /* @__PURE__ */ new Set(["li"]),
42375
+ td: /* @__PURE__ */ new Set(["td", "th"]),
42376
+ th: /* @__PURE__ */ new Set(["td", "th"]),
42377
+ tr: /* @__PURE__ */ new Set(["td", "th", "tr"]),
42378
+ thead: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
42379
+ tbody: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
42380
+ tfoot: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
42381
+ p: /* @__PURE__ */ new Set(["p"]),
42382
+ option: /* @__PURE__ */ new Set(["option"])
42383
+ };
42384
+ var TAG_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)((?:\s+(?:"[^"]*"|'[^']*'|[^<>"'])*)?)(\/)?>/g;
42385
+ var ATTR_RE = /([a-zA-Z_:][a-zA-Z0-9:._-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
42386
+ function parseAttrs(raw) {
42387
+ const attrs = {};
42388
+ if (!raw) return attrs;
42389
+ ATTR_RE.lastIndex = 0;
42390
+ let m;
42391
+ while ((m = ATTR_RE.exec(raw)) !== null) {
42392
+ const name = m[1].toLowerCase();
42393
+ let value = m[2] ?? "";
42394
+ if (value.length >= 2 && (value[0] === '"' || value[0] === "'")) {
42395
+ value = value.slice(1, -1);
42396
+ }
42397
+ attrs[name] = value;
42398
+ }
42399
+ return attrs;
42400
+ }
42401
+ function stripNonContent(html) {
42402
+ return html.replace(/<!--[\s\S]*?-->/g, "").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, "").replace(/<!doctype[^>]*>/gi, "").replace(/<script\b[\s\S]*?<\/script>/gi, "").replace(/<style\b[\s\S]*?<\/style>/gi, "");
42403
+ }
42404
+ function tokenize(html) {
42405
+ const tokens = [];
42406
+ const clean = stripNonContent(html);
42407
+ TAG_RE.lastIndex = 0;
42408
+ let lastIndex = 0;
42409
+ let m;
42410
+ while ((m = TAG_RE.exec(clean)) !== null) {
42411
+ if (m.index > lastIndex) {
42412
+ tokens.push({ kind: "text", text: clean.slice(lastIndex, m.index) });
42413
+ }
42414
+ lastIndex = TAG_RE.lastIndex;
42415
+ const isClose = m[1] === "/";
42416
+ const name = m[2].toLowerCase();
42417
+ if (isClose) {
42418
+ tokens.push({ kind: "close", name });
42419
+ } else {
42420
+ tokens.push({
42421
+ kind: "open",
42422
+ name,
42423
+ attrs: parseAttrs(m[3]),
42424
+ selfClose: m[4] === "/"
42425
+ });
42426
+ }
42427
+ }
42428
+ if (lastIndex < clean.length) {
42429
+ tokens.push({ kind: "text", text: clean.slice(lastIndex) });
42430
+ }
42431
+ return tokens;
42432
+ }
42433
+ function parseHtml(html) {
42434
+ const root = { type: "element", name: "#root", attrs: {}, children: [] };
42435
+ const stack = [root];
42436
+ const top = () => stack[stack.length - 1];
42437
+ for (const token of tokenize(html)) {
42438
+ if (token.kind === "text") {
42439
+ if (token.text) top().children.push({ type: "text", value: token.text });
42440
+ continue;
42441
+ }
42442
+ if (token.kind === "open") {
42443
+ const name = token.name;
42444
+ const closes = IMPLIED_CLOSE[name];
42445
+ if (closes) {
42446
+ while (stack.length > 1 && closes.has(top().name)) stack.pop();
42447
+ }
42448
+ const el = {
42449
+ type: "element",
42450
+ name,
42451
+ attrs: token.attrs ?? {},
42452
+ children: []
42453
+ };
42454
+ top().children.push(el);
42455
+ if (!token.selfClose && !VOID_ELEMENTS.has(name)) {
42456
+ stack.push(el);
42457
+ }
42458
+ continue;
42459
+ }
42460
+ for (let i = stack.length - 1; i > 0; i--) {
42461
+ if (stack[i].name === token.name) {
42462
+ stack.length = i;
42463
+ break;
42464
+ }
42465
+ }
42466
+ }
42467
+ return root.children;
42468
+ }
42469
+ function childElements(el, names) {
42470
+ return el.children.filter((c) => c.type === "element" && names.has(c.name));
42471
+ }
42472
+
42473
+ // src/parser/html/HtmlTableParser.ts
42474
+ var MARK_TAGS = {
42475
+ b: "bold",
42476
+ strong: "bold",
42477
+ i: "italic",
42478
+ em: "italic",
42479
+ sup: "superscript",
42480
+ sub: "subscript",
42481
+ s: "strike",
42482
+ strike: "strike",
42483
+ del: "del",
42484
+ ins: "ins",
42485
+ u: "underline",
42486
+ mark: "highlight",
42487
+ code: "code"
42488
+ };
42489
+ var SECTION_TAGS = /* @__PURE__ */ new Set(["thead", "tbody", "tfoot"]);
42490
+ var ROW_TAGS = /* @__PURE__ */ new Set(["tr"]);
42491
+ var CELL_TAGS = /* @__PURE__ */ new Set(["td", "th"]);
42492
+ var LI_TAGS = /* @__PURE__ */ new Set(["li"]);
42493
+ var VALID_SCOPES = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
42494
+ var NAMED_ENTITIES = {
42495
+ amp: "&",
42496
+ lt: "<",
42497
+ gt: ">",
42498
+ quot: '"',
42499
+ apos: "'",
42500
+ nbsp: "\xA0"
42501
+ };
42502
+ function decodeEntities(text) {
42503
+ return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, (whole, body) => {
42504
+ if (body[0] === "#") {
42505
+ const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
42506
+ if (Number.isFinite(code) && code >= 0 && code <= 1114111) {
42507
+ try {
42508
+ return String.fromCodePoint(code);
42509
+ } catch {
42510
+ return whole;
42511
+ }
42512
+ }
42513
+ return whole;
42514
+ }
42515
+ return NAMED_ENTITIES[body] ?? whole;
42516
+ });
42517
+ }
42518
+ function intAttr(value) {
42519
+ if (value == null) return void 0;
42520
+ const n = Number.parseInt(value, 10);
42521
+ return Number.isInteger(n) ? n : void 0;
42522
+ }
42523
+ function isElement(node) {
42524
+ return node.type === "element";
42525
+ }
42526
+ var HtmlTableParser = class {
42527
+ keepUnknownTags = false;
42528
+ /**
42529
+ * Parse an HTML string and return one table bit per top-level `<table>`.
42530
+ */
42531
+ parse(html, options) {
42532
+ this.keepUnknownTags = options?.keepUnknownTags ?? false;
42533
+ const tableFormat = options?.tableFormat ?? "table-extended";
42534
+ const onWarning = options?.onWarning;
42535
+ const tables = this.collectTopLevelTables(parseHtml(html));
42536
+ const bits = [];
42537
+ for (const table of tables) {
42538
+ const bit = this.tableToBit(table, tableFormat, onWarning);
42539
+ if (bit) bits.push(bit);
42540
+ }
42541
+ return bits;
42542
+ }
42543
+ // --- table extraction -----------------------------------------------------
42544
+ /** Collect every `<table>` element, NOT descending into nested tables. */
42545
+ collectTopLevelTables(nodes) {
42546
+ const tables = [];
42547
+ const visit = (node) => {
42548
+ if (!isElement(node)) return;
42549
+ if (node.name === "table") {
42550
+ tables.push(node);
42551
+ return;
42552
+ }
42553
+ node.children.forEach(visit);
42554
+ };
42555
+ nodes.forEach(visit);
42556
+ return tables;
42557
+ }
42558
+ tableToBit(table, tableFormat, onWarning) {
42559
+ const header = [];
42560
+ const body = [];
42561
+ const footer = [];
42562
+ let caption;
42563
+ for (const child of table.children) {
42564
+ if (!isElement(child)) continue;
42565
+ if (child.name === "caption") {
42566
+ const capContent = this.inlineNodes(child.children, []);
42567
+ const trimmed = this.trimInline(capContent);
42568
+ if (trimmed.length > 0) caption = trimmed;
42569
+ } else if (SECTION_TAGS.has(child.name)) {
42570
+ const target = child.name === "thead" ? header : child.name === "tfoot" ? footer : body;
42571
+ for (const tr of childElements(child, ROW_TAGS)) {
42572
+ target.push(this.rowToCells(tr));
42573
+ }
42574
+ } else if (ROW_TAGS.has(child.name)) {
42575
+ body.push(this.rowToCells(child));
42576
+ }
42577
+ }
42578
+ if (header.length === 0 && body.length === 0 && footer.length === 0) {
42579
+ return void 0;
42580
+ }
42581
+ const tableExtended = {};
42582
+ if (header.length > 0) tableExtended.header = { rows: header };
42583
+ if (body.length > 0) tableExtended.body = { rows: body };
42584
+ if (footer.length > 0) tableExtended.footer = { rows: footer };
42585
+ let tableData = tableExtended;
42586
+ if (tableFormat === "table") {
42587
+ if (this.isLossyAsBasic(tableExtended)) {
42588
+ onWarning?.(
42589
+ "Converting an HTML table to [.table] loses rowspan/colspan/scope and multi-section structure; use --tableFormat table-extended to preserve them."
42590
+ );
42591
+ }
42592
+ tableData = convertExtendedToBasicTableFormat(tableExtended);
42593
+ }
42594
+ const bit = {
42595
+ type: tableFormat,
42596
+ format: "bitmark++",
42597
+ table: tableData
42598
+ };
42599
+ if (caption) bit.caption = caption;
42600
+ return bit;
42601
+ }
42602
+ rowToCells(tr) {
42603
+ const cells = [];
42604
+ for (const cell of childElements(tr, CELL_TAGS)) {
42605
+ cells.push(this.cellToJson(cell));
42606
+ }
42607
+ return { cells };
42608
+ }
42609
+ cellToJson(cell) {
42610
+ const json = {
42611
+ content: this.blockNodes(cell.children)
42612
+ };
42613
+ if (cell.name === "th") json.title = true;
42614
+ const rowspan = intAttr(cell.attrs.rowspan);
42615
+ if (rowspan != null && rowspan >= 2) json.rowspan = rowspan;
42616
+ const colspan = intAttr(cell.attrs.colspan);
42617
+ if (colspan != null && colspan >= 2) json.colspan = colspan;
42618
+ const scope = cell.attrs.scope?.toLowerCase();
42619
+ if (scope && VALID_SCOPES.has(scope)) {
42620
+ json.scope = scope;
42621
+ }
42622
+ const width = this.widthFromCell(cell);
42623
+ if (width != null) json.colwidth = width;
42624
+ return json;
42625
+ }
42626
+ widthFromCell(cell) {
42627
+ const attrWidth = intAttr(cell.attrs.width);
42628
+ if (attrWidth != null && attrWidth > 0) return attrWidth;
42629
+ const style = cell.attrs.style;
42630
+ if (style) {
42631
+ const m = style.match(/width\s*:\s*(\d+)\s*px/i);
42632
+ if (m) {
42633
+ const n = Number.parseInt(m[1], 10);
42634
+ if (Number.isInteger(n) && n > 0) return n;
42635
+ }
42636
+ }
42637
+ return void 0;
42638
+ }
42639
+ isLossyAsBasic(table) {
42640
+ if (table.footer) return true;
42641
+ if (table.header && table.header.rows.length > 1) return true;
42642
+ const sections = [table.header, table.body, table.footer];
42643
+ for (const section of sections) {
42644
+ if (!section) continue;
42645
+ for (const row of section.rows) {
42646
+ for (const cell of row.cells) {
42647
+ if (cell.rowspan || cell.colspan || cell.scope) return true;
42648
+ }
42649
+ }
42650
+ }
42651
+ return false;
42652
+ }
42653
+ // --- content conversion (HTML -> TextAst) ---------------------------------
42654
+ /**
42655
+ * Convert a cell/li's child nodes into block-level TextAst nodes (paragraphs, lists, images).
42656
+ */
42657
+ blockNodes(nodes) {
42658
+ const blocks = [];
42659
+ let inline = [];
42660
+ const flush = () => {
42661
+ const trimmed = this.trimInline(inline);
42662
+ inline = [];
42663
+ if (trimmed.length === 0) return;
42664
+ if (trimmed.length === 1 && trimmed[0].type === "imageInline") {
42665
+ blocks.push({ ...trimmed[0], type: "image" });
42666
+ } else {
42667
+ blocks.push({ type: "paragraph", content: trimmed, attrs: {} });
42668
+ }
42669
+ };
42670
+ for (const node of nodes) {
42671
+ if (!isElement(node)) {
42672
+ inline.push(...this.inlineText(node.value, []));
42673
+ continue;
42674
+ }
42675
+ const name = node.name;
42676
+ if (name === "p") {
42677
+ flush();
42678
+ const content = this.trimInline(this.inlineNodes(node.children, []));
42679
+ if (content.length > 0) {
42680
+ blocks.push({ type: "paragraph", content, attrs: {} });
42681
+ }
42682
+ } else if (this.isListTag(name)) {
42683
+ flush();
42684
+ const list = this.listNode(node);
42685
+ if (list) blocks.push(list);
42686
+ } else {
42687
+ inline.push(...this.inlineFromElement(node, []));
42688
+ }
42689
+ }
42690
+ flush();
42691
+ return blocks;
42692
+ }
42693
+ /** Convert child nodes to a flat list of inline TextNodes, applying the given marks. */
42694
+ inlineNodes(nodes, marks) {
42695
+ const out = [];
42696
+ for (const node of nodes) {
42697
+ if (!isElement(node)) {
42698
+ out.push(...this.inlineText(node.value, marks));
42699
+ } else {
42700
+ out.push(...this.inlineFromElement(node, marks));
42701
+ }
42702
+ }
42703
+ return out;
42704
+ }
42705
+ inlineFromElement(el, marks) {
42706
+ const name = el.name;
42707
+ if (name === "br") {
42708
+ return [{ type: "hardBreak" }];
42709
+ }
42710
+ if (name === "img") {
42711
+ return [this.imageNode(el, true)];
42712
+ }
42713
+ const markType = MARK_TAGS[name];
42714
+ if (markType) {
42715
+ return this.inlineNodes(el.children, [...marks, { type: markType }]);
42716
+ }
42717
+ if (name === "a") {
42718
+ const linkMark = {
42719
+ type: "link",
42720
+ attrs: {
42721
+ href: el.attrs.href ?? "",
42722
+ ...el.attrs.target ? { target: el.attrs.target } : {}
42723
+ }
42724
+ };
42725
+ return this.inlineNodes(el.children, [...marks, linkMark]);
42726
+ }
42727
+ if (this.keepUnknownTags) {
42728
+ const open = this.serializeOpenTag(el);
42729
+ const close = `</${name}>`;
42730
+ return [
42731
+ ...this.inlineText(open, marks),
42732
+ ...this.inlineNodes(el.children, marks),
42733
+ ...this.inlineText(close, marks)
42734
+ ];
42735
+ }
42736
+ return this.inlineNodes(el.children, marks);
42737
+ }
42738
+ inlineText(raw, marks) {
42739
+ const text = decodeEntities(raw).replace(/\s+/g, " ");
42740
+ if (text === "") return [];
42741
+ const node = { text, type: "text" };
42742
+ if (marks.length > 0) node.marks = marks.map((m) => ({ ...m }));
42743
+ return [node];
42744
+ }
42745
+ serializeOpenTag(el) {
42746
+ const attrs = Object.entries(el.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${v}"`).join("");
42747
+ return `<${el.name}${attrs}>`;
42748
+ }
42749
+ // --- lists ----------------------------------------------------------------
42750
+ isListTag(name) {
42751
+ return name === "ul" || name === "ol";
42752
+ }
42753
+ listNode(el) {
42754
+ const items = childElements(el, LI_TAGS);
42755
+ if (items.length === 0) return void 0;
42756
+ const isTask = el.name === "ul" && items.some((li) => {
42757
+ const firstEl = li.children.find(isElement);
42758
+ return firstEl?.name === "input" && firstEl.attrs.type?.toLowerCase() === "checkbox";
42759
+ });
42760
+ if (isTask) {
42761
+ return {
42762
+ type: "taskList",
42763
+ content: items.map((li) => this.taskItemNode(li))
42764
+ };
42765
+ }
42766
+ const type = this.listType(el);
42767
+ const node = {
42768
+ type,
42769
+ content: items.map((li) => this.listItemNode(li))
42770
+ };
42771
+ if (el.name === "ol") {
42772
+ const start = intAttr(el.attrs.start);
42773
+ if (start != null) {
42774
+ node.attrs = { start };
42775
+ }
42776
+ }
42777
+ return node;
42778
+ }
42779
+ listType(el) {
42780
+ if (el.name === "ol") {
42781
+ switch (el.attrs.type) {
42782
+ case "i":
42783
+ return "orderedListRomanLower";
42784
+ case "I":
42785
+ return "orderedListRoman";
42786
+ case "a":
42787
+ return "letteredListLower";
42788
+ case "A":
42789
+ return "letteredList";
42790
+ default:
42791
+ return "orderedList";
42792
+ }
42793
+ }
42794
+ const style = el.attrs.style ?? "";
42795
+ if (/list-style(-type)?\s*:\s*none/i.test(style)) return "noBulletList";
42796
+ return "bulletList";
42797
+ }
42798
+ listItemNode(li) {
42799
+ let content = this.blockNodes(li.children);
42800
+ if (content.length === 0) {
42801
+ content = [{ type: "paragraph", content: [], attrs: {} }];
42802
+ }
42803
+ return { type: "listItem", content };
42804
+ }
42805
+ taskItemNode(li) {
42806
+ const checkbox = li.children.find(
42807
+ (c) => isElement(c) && c.name === "input" && c.attrs.type?.toLowerCase() === "checkbox"
42808
+ );
42809
+ const checked = checkbox ? "checked" in checkbox.attrs : false;
42810
+ const rest = li.children.filter((c) => c !== checkbox);
42811
+ let content = this.blockNodes(rest);
42812
+ if (content.length === 0) {
42813
+ content = [{ type: "paragraph", content: [], attrs: {} }];
42814
+ }
42815
+ return { type: "taskItem", attrs: { checked }, content };
42816
+ }
42817
+ // --- images ---------------------------------------------------------------
42818
+ imageNode(el, inline) {
42819
+ const attrs = { src: el.attrs.src ?? "" };
42820
+ attrs.alt = el.attrs.alt ?? null;
42821
+ attrs.title = el.attrs.title ?? null;
42822
+ const width = intAttr(el.attrs.width);
42823
+ if (width != null) attrs.width = width;
42824
+ const height = intAttr(el.attrs.height);
42825
+ if (height != null) attrs.height = height;
42826
+ if (el.attrs.class) attrs.class = el.attrs.class;
42827
+ return { type: inline ? "imageInline" : "image", attrs };
42828
+ }
42829
+ // --- whitespace -----------------------------------------------------------
42830
+ /** Trim leading/trailing whitespace from inline content (edges only). */
42831
+ trimInline(nodes) {
42832
+ const out = nodes.slice();
42833
+ const isWs = (n) => n.type === "text" && /^\s*$/.test(n.text ?? "");
42834
+ while (out.length > 0 && isWs(out[0])) out.shift();
42835
+ if (out.length > 0 && out[0].type === "text") {
42836
+ out[0] = { ...out[0], text: (out[0].text ?? "").replace(/^\s+/, "") };
42837
+ }
42838
+ while (out.length > 0 && isWs(out[out.length - 1])) out.pop();
42839
+ if (out.length > 0 && out[out.length - 1].type === "text") {
42840
+ const last = out.length - 1;
42841
+ out[last] = { ...out[last], text: (out[last].text ?? "").replace(/\s+$/, "") };
42842
+ }
42843
+ return out.filter((n) => !(n.type === "text" && n.text === ""));
42844
+ }
42845
+ };
41890
42846
  var StreamWriter = class {
41891
42847
  _stream;
41892
42848
  endOfLineString = os.EOL;
@@ -42140,248 +43096,21 @@ var BitmarkFileGenerator = class {
42140
43096
  /**
42141
43097
  * Generate bitmark markup from bitmark AST as a file
42142
43098
  *
42143
- * @param ast bitmark AST
42144
- */
42145
- async generate(ast) {
42146
- return this.generator.generate(ast);
42147
- }
42148
- /**
42149
- * Generate bitmark markup from bitmark AST as a file synchronously
42150
- *
42151
- * @param ast bitmark AST
42152
- */
42153
- generateSync(_ast) {
42154
- if (this.async) {
42155
- throw new Error("Sync operation not supported");
42156
- }
42157
- this.generator.generateSync(_ast);
42158
- }
42159
- };
42160
-
42161
- // src/generator/html/HtmlTableGenerator.ts
42162
- var MARK_TO_TAG = {
42163
- bold: "b",
42164
- italic: "i",
42165
- superscript: "sup",
42166
- subscript: "sub",
42167
- strike: "s",
42168
- del: "del",
42169
- ins: "ins",
42170
- underline: "u",
42171
- highlight: "mark",
42172
- code: "code"
42173
- };
42174
- var HtmlTableGenerator = class {
42175
- indentUnit = " ";
42176
- /**
42177
- * Generate HTML table fragments from a string or JSON value.
42178
- * @returns the concatenated `<table>` fragments, blank-line separated (empty string if none).
42179
- */
42180
- generate(input) {
42181
- let data = input;
42182
- if (typeof data === "string") {
42183
- try {
42184
- data = JSON.parse(data);
42185
- } catch {
42186
- return "";
42187
- }
42188
- }
42189
- const bits = this.collectBits(data);
42190
- const fragments = [];
42191
- for (const bit of bits) {
42192
- const html = this.renderTableBit(bit);
42193
- if (html) fragments.push(html);
42194
- }
42195
- return fragments.join("\n\n");
42196
- }
42197
- // --- bit collection -------------------------------------------------------
42198
- collectBits(data) {
42199
- const out = [];
42200
- const visit = (value) => {
42201
- if (value == null || typeof value !== "object") return;
42202
- if (Array.isArray(value)) {
42203
- value.forEach(visit);
42204
- return;
42205
- }
42206
- const obj = value;
42207
- if ("bit" in obj && obj.bit && typeof obj.bit === "object") {
42208
- out.push(obj.bit);
42209
- } else if ("type" in obj) {
42210
- out.push(obj);
42211
- }
42212
- };
42213
- visit(data);
42214
- return out;
42215
- }
42216
- // --- table rendering ------------------------------------------------------
42217
- renderTableBit(bit) {
42218
- const table = bit.table;
42219
- if (!table) return "";
42220
- if (bit.type !== "table" && bit.type !== "table-extended") return "";
42221
- const extended = this.toExtended(table);
42222
- const lines = [];
42223
- lines.push("<table>");
42224
- if (bit.caption != null) {
42225
- const caption = this.cellContentToHtml(this.asTextAst(bit.caption));
42226
- if (caption) lines.push(`${this.indentUnit}<caption>${caption}</caption>`);
42227
- }
42228
- this.renderSection(lines, "thead", extended.header, "header");
42229
- this.renderSection(lines, "tbody", extended.body, "body");
42230
- this.renderSection(lines, "tfoot", extended.footer, "footer");
42231
- lines.push("</table>");
42232
- return lines.join("\n");
42233
- }
42234
- toExtended(table) {
42235
- const t = table;
42236
- if (t.header || t.body || t.footer) return table;
42237
- return convertBasicToExtendedTableFormat(table);
42238
- }
42239
- renderSection(lines, tag, section, key) {
42240
- if (!section || !section.rows || section.rows.length === 0) return;
42241
- lines.push(`${this.indentUnit}<${tag}>`);
42242
- for (const row of section.rows) {
42243
- lines.push(`${this.indentUnit.repeat(2)}<tr>`);
42244
- for (const cell of row.cells ?? []) {
42245
- lines.push(`${this.indentUnit.repeat(3)}${this.renderCell(cell, key)}`);
42246
- }
42247
- lines.push(`${this.indentUnit.repeat(2)}</tr>`);
42248
- }
42249
- lines.push(`${this.indentUnit}</${tag}>`);
42250
- }
42251
- renderCell(cell, _key) {
42252
- const tag = cell.title === true ? "th" : "td";
42253
- const attrs = [];
42254
- if (cell.rowspan && cell.rowspan > 1) attrs.push(`rowspan="${cell.rowspan}"`);
42255
- if (cell.colspan && cell.colspan > 1) attrs.push(`colspan="${cell.colspan}"`);
42256
- if (cell.scope) attrs.push(`scope="${cell.scope}"`);
42257
- if (cell.colwidth && cell.colwidth > 0) attrs.push(`width="${cell.colwidth}"`);
42258
- const open = attrs.length > 0 ? `<${tag} ${attrs.join(" ")}>` : `<${tag}>`;
42259
- return `${open}${this.cellContentToHtml(this.asTextAst(cell.content))}</${tag}>`;
42260
- }
42261
- // --- cell content (TextAst -> HTML) ---------------------------------------
42262
- asTextAst(content) {
42263
- if (content == null) return [];
42264
- if (typeof content === "string") return [{ type: "text", text: content }];
42265
- return content;
42266
- }
42267
- cellContentToHtml(blocks) {
42268
- if (blocks.length === 0) return "";
42269
- if (blocks.length === 1 && blocks[0].type === "paragraph") {
42270
- return this.inlineToHtml(blocks[0].content ?? []);
42271
- }
42272
- return blocks.map((b) => this.blockToHtml(b)).join("");
42273
- }
42274
- blockToHtml(node) {
42275
- switch (node.type) {
42276
- case "paragraph":
42277
- return `<p>${this.inlineToHtml(node.content ?? [])}</p>`;
42278
- case "bulletList":
42279
- case "noBulletList":
42280
- case "orderedList":
42281
- case "orderedListRoman":
42282
- case "orderedListRomanLower":
42283
- case "letteredList":
42284
- case "letteredListLower":
42285
- case "taskList":
42286
- return this.listToHtml(node);
42287
- case "image":
42288
- case "imageInline":
42289
- return this.imageToHtml(node);
42290
- default:
42291
- return this.inlineToHtml([node]);
42292
- }
42293
- }
42294
- inlineToHtml(nodes) {
42295
- return nodes.map((n) => this.inlineNodeToHtml(n)).join("");
42296
- }
42297
- inlineNodeToHtml(node) {
42298
- if (node.type === "hardBreak") return "<br>";
42299
- if (node.type === "image" || node.type === "imageInline") return this.imageToHtml(node);
42300
- if (node.type === "text") {
42301
- return this.wrapMarks(this.escapeText(node.text ?? ""), node.marks);
42302
- }
42303
- if (node.content) return this.inlineToHtml(node.content);
42304
- return this.escapeText(node.text ?? "");
42305
- }
42306
- wrapMarks(text, marks) {
42307
- if (!marks || marks.length === 0) return text;
42308
- let open = "";
42309
- let close = "";
42310
- for (const mark of marks) {
42311
- if (mark.type === "link") {
42312
- const href = mark.attrs?.href ?? "";
42313
- open += `<a href="${this.escapeAttr(href)}">`;
42314
- close = `</a>${close}`;
42315
- } else {
42316
- const tag = MARK_TO_TAG[mark.type];
42317
- if (tag) {
42318
- open += `<${tag}>`;
42319
- close = `</${tag}>${close}`;
42320
- }
42321
- }
42322
- }
42323
- return `${open}${text}${close}`;
42324
- }
42325
- // --- lists ----------------------------------------------------------------
42326
- listToHtml(node) {
42327
- const { tag, attrs } = this.listTag(node);
42328
- const items = (node.content ?? []).map((item) => this.listItemToHtml(item, node.type === "taskList")).join("");
42329
- return `<${tag}${attrs}>${items}</${tag}>`;
42330
- }
42331
- listTag(node) {
42332
- const start = node.attrs?.start;
42333
- const startAttr = typeof start === "number" && start !== 1 ? ` start="${start}"` : "";
42334
- switch (node.type) {
42335
- case "orderedList":
42336
- return { tag: "ol", attrs: startAttr };
42337
- case "orderedListRoman":
42338
- return { tag: "ol", attrs: ` type="I"${startAttr}` };
42339
- case "orderedListRomanLower":
42340
- return { tag: "ol", attrs: ` type="i"${startAttr}` };
42341
- case "letteredList":
42342
- return { tag: "ol", attrs: ` type="A"${startAttr}` };
42343
- case "letteredListLower":
42344
- return { tag: "ol", attrs: ` type="a"${startAttr}` };
42345
- case "noBulletList":
42346
- return { tag: "ul", attrs: ' style="list-style:none"' };
42347
- case "taskList":
42348
- case "bulletList":
42349
- default:
42350
- return { tag: "ul", attrs: "" };
42351
- }
42352
- }
42353
- listItemToHtml(item, task) {
42354
- const inner = (item.content ?? []).map((b) => {
42355
- if (b.type === "paragraph") return this.inlineToHtml(b.content ?? []);
42356
- return this.blockToHtml(b);
42357
- }).join("");
42358
- if (task) {
42359
- const checked = item.attrs?.checked ? " checked" : "";
42360
- return `<li><input type="checkbox"${checked}>${inner}</li>`;
42361
- }
42362
- return `<li>${inner}</li>`;
42363
- }
42364
- // --- images ---------------------------------------------------------------
42365
- imageToHtml(node) {
42366
- const attrs = node.attrs ?? {};
42367
- const parts = [];
42368
- const src = attrs.src;
42369
- parts.push(`src="${this.escapeAttr(typeof src === "string" ? src : "")}"`);
42370
- if (typeof attrs.alt === "string") parts.push(`alt="${this.escapeAttr(attrs.alt)}"`);
42371
- if (typeof attrs.title === "string") parts.push(`title="${this.escapeAttr(attrs.title)}"`);
42372
- if (attrs.width != null) parts.push(`width="${attrs.width}"`);
42373
- if (attrs.height != null) parts.push(`height="${attrs.height}"`);
42374
- if (typeof attrs.class === "string" && attrs.class) {
42375
- parts.push(`class="${this.escapeAttr(attrs.class)}"`);
42376
- }
42377
- return `<img ${parts.join(" ")}>`;
42378
- }
42379
- // --- escaping -------------------------------------------------------------
42380
- escapeText(text) {
42381
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
43099
+ * @param ast bitmark AST
43100
+ */
43101
+ async generate(ast) {
43102
+ return this.generator.generate(ast);
42382
43103
  }
42383
- escapeAttr(text) {
42384
- return this.escapeText(text).replace(/"/g, "&quot;");
43104
+ /**
43105
+ * Generate bitmark markup from bitmark AST as a file synchronously
43106
+ *
43107
+ * @param ast bitmark AST
43108
+ */
43109
+ generateSync(_ast) {
43110
+ if (this.async) {
43111
+ throw new Error("Sync operation not supported");
43112
+ }
43113
+ this.generator.generateSync(_ast);
42385
43114
  }
42386
43115
  };
42387
43116
  var JsonFileGenerator = class {
@@ -42800,6 +43529,7 @@ var ConfigBuilder = class {
42800
43529
  k = stringUtils.camelToKebab(k);
42801
43530
  const resolvedGroups = resolveGroupReferences(k);
42802
43531
  const hasOuterExport = Object.prototype.hasOwnProperty.call(tag, "exportJsonKey");
43532
+ const hasOuterHtml = Object.prototype.hasOwnProperty.call(tag, "htmlKey");
42803
43533
  if (!hasOuterExport && tag.jsonKey != null) {
42804
43534
  const headingPath = [...pathStack, `tags.${tag.key}`].join(" / ");
42805
43535
  exportJsonKeyErrors.push(
@@ -42809,6 +43539,8 @@ var ConfigBuilder = class {
42809
43539
  for (const ref of resolvedGroups) {
42810
43540
  const exportJsonKeyChosen = hasOuterExport ? tag.exportJsonKey : ref.exportJsonKey;
42811
43541
  const hasExportChosen = hasOuterExport || !!ref.hasExportJsonKey;
43542
+ const htmlKeyChosen = hasOuterHtml ? tag.htmlKey : ref.htmlKey;
43543
+ const hasHtmlChosen = hasOuterHtml || !!ref.hasHtmlKey;
42812
43544
  const min = tag.minCount != null ? tag.minCount : ref.min;
42813
43545
  const max = tag.maxCount != null ? tag.maxCount : ref.max;
42814
43546
  const description = tag.description ? tag.description : ref.description;
@@ -42816,6 +43548,7 @@ var ConfigBuilder = class {
42816
43548
  type: "group",
42817
43549
  key: ref.key,
42818
43550
  ...hasExportChosen ? { jsonKey: exportJsonKeyChosen } : {},
43551
+ ...hasHtmlChosen ? { htmlKey: htmlKeyChosen } : {},
42819
43552
  ...min != null ? { min } : {},
42820
43553
  ...max != null ? { max } : {},
42821
43554
  ...description ? { description } : {}
@@ -42844,10 +43577,13 @@ var ConfigBuilder = class {
42844
43577
  );
42845
43578
  exportJsonKeyField = {};
42846
43579
  }
43580
+ const hasHtml = Object.prototype.hasOwnProperty.call(tag, "htmlKey");
43581
+ const htmlKeyField = hasHtml ? { htmlKey: tag.htmlKey } : {};
42847
43582
  const t = {
42848
43583
  type: "tag",
42849
43584
  key: tagName,
42850
43585
  ...exportJsonKeyField,
43586
+ ...htmlKeyField,
42851
43587
  format,
42852
43588
  default: tag.defaultValue ?? null,
42853
43589
  ...tag.nullable ? { nullable: true } : {},
@@ -42870,6 +43606,13 @@ var ConfigBuilder = class {
42870
43606
  );
42871
43607
  return {};
42872
43608
  };
43609
+ const cardHtmlKeyField = (container, htmlExportKey) => {
43610
+ const c = container;
43611
+ if (Object.prototype.hasOwnProperty.call(c, htmlExportKey)) {
43612
+ return { htmlKey: c[htmlExportKey] };
43613
+ }
43614
+ return {};
43615
+ };
42873
43616
  const serializeCardSet = (cardSetKey) => {
42874
43617
  const variantBodyFormat = (variant) => variant.format ?? TextFormat.bitmarkText;
42875
43618
  const cardSetConfig = CARDSETS[cardSetKey];
@@ -42900,6 +43643,7 @@ var ConfigBuilder = class {
42900
43643
  }
42901
43644
  return {
42902
43645
  ...cardJsonKeyField(variant, "jsonKey", "exportJsonKey", variantPath),
43646
+ ...cardHtmlKeyField(variant, "htmlKey"),
42903
43647
  format: variantBodyFormat(variant),
42904
43648
  tags: variantTags,
42905
43649
  repeatCount: variant.repeatCount ?? 1,
@@ -42911,6 +43655,7 @@ var ConfigBuilder = class {
42911
43655
  name: side.name,
42912
43656
  ...side.repeat ? { repeat: side.repeat } : {},
42913
43657
  ...cardJsonKeyField(side, "jsonKey", "exportJsonKey", sidePath),
43658
+ ...cardHtmlKeyField(side, "htmlKey"),
42914
43659
  variants
42915
43660
  };
42916
43661
  });
@@ -42918,20 +43663,23 @@ var ConfigBuilder = class {
42918
43663
  const cards = Object.entries(cardSetConfig.sections).map(([sectionName, section]) => {
42919
43664
  const sectionPath = `cardSets.${normalizedKey} / cards.${sectionName}`;
42920
43665
  const hasSideExport = Object.prototype.hasOwnProperty.call(section, "sideExportJsonKey");
43666
+ const hasSideHtml = Object.prototype.hasOwnProperty.call(section, "sideHtmlKey");
42921
43667
  let cardSides = sides;
42922
- if (hasSideExport || section.sideJsonKey != null) {
43668
+ if (hasSideExport || section.sideJsonKey != null || hasSideHtml) {
42923
43669
  const sideOverride = cardJsonKeyField(
42924
43670
  section,
42925
43671
  "sideJsonKey",
42926
43672
  "sideExportJsonKey",
42927
43673
  `${sectionPath} / sides.*`
42928
43674
  );
42929
- cardSides = sides.map((s) => ({ ...s, ...sideOverride }));
43675
+ const sideHtmlOverride = cardHtmlKeyField(section, "sideHtmlKey");
43676
+ cardSides = sides.map((s) => ({ ...s, ...sideOverride, ...sideHtmlOverride }));
42930
43677
  }
42931
43678
  return {
42932
43679
  name: sectionName,
42933
43680
  ...section.isDefault ? { isDefault: true } : {},
42934
43681
  ...cardJsonKeyField(section, "jsonKey", "exportJsonKey", sectionPath),
43682
+ ...cardHtmlKeyField(section, "htmlKey"),
42935
43683
  // PLAN-085: per-section cardinality. Emit only when non-default
42936
43684
  // (treat `0` / undefined as "unbounded — omit").
42937
43685
  ...section.minCount != null && section.minCount !== 0 ? { min: section.minCount } : {},
@@ -42939,7 +43687,7 @@ var ConfigBuilder = class {
42939
43687
  sides: cardSides
42940
43688
  };
42941
43689
  });
42942
- return { name: normalizedKey, cards };
43690
+ return { name: normalizedKey, ...cardHtmlKeyField(cardSetConfig, "htmlKey"), cards };
42943
43691
  }
42944
43692
  const cardSetPath = `cardSets.${normalizedKey} / cards.default`;
42945
43693
  return {
@@ -42949,6 +43697,7 @@ var ConfigBuilder = class {
42949
43697
  name: "default",
42950
43698
  isDefault: true,
42951
43699
  ...cardJsonKeyField(cardSetConfig, "jsonKey", "exportJsonKey", cardSetPath),
43700
+ ...cardHtmlKeyField(cardSetConfig, "htmlKey"),
42952
43701
  sides
42953
43702
  }
42954
43703
  ]
@@ -42988,6 +43737,10 @@ var ConfigBuilder = class {
42988
43737
  ref.exportJsonKey = t.jsonKey;
42989
43738
  ref.hasExportJsonKey = true;
42990
43739
  }
43740
+ if (Object.prototype.hasOwnProperty.call(t, "htmlKey")) {
43741
+ ref.htmlKey = t.htmlKey;
43742
+ ref.hasHtmlKey = true;
43743
+ }
42991
43744
  replacements.push(ref);
42992
43745
  }
42993
43746
  squashedGroups.set(groupKey, replacements);
@@ -43022,6 +43775,7 @@ var ConfigBuilder = class {
43022
43775
  type: "group",
43023
43776
  key: ref.key,
43024
43777
  ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
43778
+ ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
43025
43779
  ...ref.min != null ? { min: ref.min } : {},
43026
43780
  ...ref.max != null ? { max: ref.max } : {},
43027
43781
  ...ref.description ? { description: ref.description } : {}
@@ -43035,6 +43789,7 @@ var ConfigBuilder = class {
43035
43789
  type: "group",
43036
43790
  key: ref.key,
43037
43791
  ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
43792
+ ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
43038
43793
  ...ref.min != null ? { min: ref.min } : {},
43039
43794
  ...ref.max != null ? { max: ref.max } : {},
43040
43795
  ...ref.description ? { description: ref.description } : {}
@@ -43164,6 +43919,7 @@ var ConfigBuilder = class {
43164
43919
  type: "group",
43165
43920
  key: ref.key,
43166
43921
  ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
43922
+ ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
43167
43923
  ...ref.min != null ? { min: ref.min } : {},
43168
43924
  ...ref.max != null ? { max: ref.max } : {},
43169
43925
  ...ref.description ? { description: ref.description } : {}
@@ -43353,497 +44109,6 @@ var ConfigBuilder = class {
43353
44109
  }
43354
44110
  };
43355
44111
 
43356
- // src/parser/html/HtmlDom.ts
43357
- var VOID_ELEMENTS = /* @__PURE__ */ new Set([
43358
- "area",
43359
- "base",
43360
- "br",
43361
- "col",
43362
- "embed",
43363
- "hr",
43364
- "img",
43365
- "input",
43366
- "link",
43367
- "meta",
43368
- "param",
43369
- "source",
43370
- "track",
43371
- "wbr"
43372
- ]);
43373
- var IMPLIED_CLOSE = {
43374
- li: /* @__PURE__ */ new Set(["li"]),
43375
- td: /* @__PURE__ */ new Set(["td", "th"]),
43376
- th: /* @__PURE__ */ new Set(["td", "th"]),
43377
- tr: /* @__PURE__ */ new Set(["td", "th", "tr"]),
43378
- thead: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
43379
- tbody: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
43380
- tfoot: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
43381
- p: /* @__PURE__ */ new Set(["p"]),
43382
- option: /* @__PURE__ */ new Set(["option"])
43383
- };
43384
- var TAG_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)((?:\s+(?:"[^"]*"|'[^']*'|[^<>"'])*)?)(\/)?>/g;
43385
- var ATTR_RE = /([a-zA-Z_:][a-zA-Z0-9:._-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
43386
- function parseAttrs(raw) {
43387
- const attrs = {};
43388
- if (!raw) return attrs;
43389
- ATTR_RE.lastIndex = 0;
43390
- let m;
43391
- while ((m = ATTR_RE.exec(raw)) !== null) {
43392
- const name = m[1].toLowerCase();
43393
- let value = m[2] ?? "";
43394
- if (value.length >= 2 && (value[0] === '"' || value[0] === "'")) {
43395
- value = value.slice(1, -1);
43396
- }
43397
- attrs[name] = value;
43398
- }
43399
- return attrs;
43400
- }
43401
- function stripNonContent(html) {
43402
- return html.replace(/<!--[\s\S]*?-->/g, "").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, "").replace(/<!doctype[^>]*>/gi, "").replace(/<script\b[\s\S]*?<\/script>/gi, "").replace(/<style\b[\s\S]*?<\/style>/gi, "");
43403
- }
43404
- function tokenize(html) {
43405
- const tokens = [];
43406
- const clean = stripNonContent(html);
43407
- TAG_RE.lastIndex = 0;
43408
- let lastIndex = 0;
43409
- let m;
43410
- while ((m = TAG_RE.exec(clean)) !== null) {
43411
- if (m.index > lastIndex) {
43412
- tokens.push({ kind: "text", text: clean.slice(lastIndex, m.index) });
43413
- }
43414
- lastIndex = TAG_RE.lastIndex;
43415
- const isClose = m[1] === "/";
43416
- const name = m[2].toLowerCase();
43417
- if (isClose) {
43418
- tokens.push({ kind: "close", name });
43419
- } else {
43420
- tokens.push({
43421
- kind: "open",
43422
- name,
43423
- attrs: parseAttrs(m[3]),
43424
- selfClose: m[4] === "/"
43425
- });
43426
- }
43427
- }
43428
- if (lastIndex < clean.length) {
43429
- tokens.push({ kind: "text", text: clean.slice(lastIndex) });
43430
- }
43431
- return tokens;
43432
- }
43433
- function parseHtml(html) {
43434
- const root = { type: "element", name: "#root", attrs: {}, children: [] };
43435
- const stack = [root];
43436
- const top = () => stack[stack.length - 1];
43437
- for (const token of tokenize(html)) {
43438
- if (token.kind === "text") {
43439
- if (token.text) top().children.push({ type: "text", value: token.text });
43440
- continue;
43441
- }
43442
- if (token.kind === "open") {
43443
- const name = token.name;
43444
- const closes = IMPLIED_CLOSE[name];
43445
- if (closes) {
43446
- while (stack.length > 1 && closes.has(top().name)) stack.pop();
43447
- }
43448
- const el = {
43449
- type: "element",
43450
- name,
43451
- attrs: token.attrs ?? {},
43452
- children: []
43453
- };
43454
- top().children.push(el);
43455
- if (!token.selfClose && !VOID_ELEMENTS.has(name)) {
43456
- stack.push(el);
43457
- }
43458
- continue;
43459
- }
43460
- for (let i = stack.length - 1; i > 0; i--) {
43461
- if (stack[i].name === token.name) {
43462
- stack.length = i;
43463
- break;
43464
- }
43465
- }
43466
- }
43467
- return root.children;
43468
- }
43469
- function childElements(el, names) {
43470
- return el.children.filter((c) => c.type === "element" && names.has(c.name));
43471
- }
43472
-
43473
- // src/parser/html/HtmlTableParser.ts
43474
- var MARK_TAGS = {
43475
- b: "bold",
43476
- strong: "bold",
43477
- i: "italic",
43478
- em: "italic",
43479
- sup: "superscript",
43480
- sub: "subscript",
43481
- s: "strike",
43482
- strike: "strike",
43483
- del: "del",
43484
- ins: "ins",
43485
- u: "underline",
43486
- mark: "highlight",
43487
- code: "code"
43488
- };
43489
- var SECTION_TAGS = /* @__PURE__ */ new Set(["thead", "tbody", "tfoot"]);
43490
- var ROW_TAGS = /* @__PURE__ */ new Set(["tr"]);
43491
- var CELL_TAGS = /* @__PURE__ */ new Set(["td", "th"]);
43492
- var LI_TAGS = /* @__PURE__ */ new Set(["li"]);
43493
- var VALID_SCOPES = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
43494
- var NAMED_ENTITIES = {
43495
- amp: "&",
43496
- lt: "<",
43497
- gt: ">",
43498
- quot: '"',
43499
- apos: "'",
43500
- nbsp: "\xA0"
43501
- };
43502
- function decodeEntities(text) {
43503
- return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, (whole, body) => {
43504
- if (body[0] === "#") {
43505
- const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
43506
- if (Number.isFinite(code) && code >= 0 && code <= 1114111) {
43507
- try {
43508
- return String.fromCodePoint(code);
43509
- } catch {
43510
- return whole;
43511
- }
43512
- }
43513
- return whole;
43514
- }
43515
- return NAMED_ENTITIES[body] ?? whole;
43516
- });
43517
- }
43518
- function intAttr(value) {
43519
- if (value == null) return void 0;
43520
- const n = Number.parseInt(value, 10);
43521
- return Number.isInteger(n) ? n : void 0;
43522
- }
43523
- function isElement(node) {
43524
- return node.type === "element";
43525
- }
43526
- var HtmlTableParser = class {
43527
- keepUnknownTags = false;
43528
- /**
43529
- * Parse an HTML string and return one table bit per top-level `<table>`.
43530
- */
43531
- parse(html, options) {
43532
- this.keepUnknownTags = options?.keepUnknownTags ?? false;
43533
- const tableFormat = options?.tableFormat ?? "table-extended";
43534
- const onWarning = options?.onWarning;
43535
- const tables = this.collectTopLevelTables(parseHtml(html));
43536
- const bits = [];
43537
- for (const table of tables) {
43538
- const bit = this.tableToBit(table, tableFormat, onWarning);
43539
- if (bit) bits.push(bit);
43540
- }
43541
- return bits;
43542
- }
43543
- // --- table extraction -----------------------------------------------------
43544
- /** Collect every `<table>` element, NOT descending into nested tables. */
43545
- collectTopLevelTables(nodes) {
43546
- const tables = [];
43547
- const visit = (node) => {
43548
- if (!isElement(node)) return;
43549
- if (node.name === "table") {
43550
- tables.push(node);
43551
- return;
43552
- }
43553
- node.children.forEach(visit);
43554
- };
43555
- nodes.forEach(visit);
43556
- return tables;
43557
- }
43558
- tableToBit(table, tableFormat, onWarning) {
43559
- const header = [];
43560
- const body = [];
43561
- const footer = [];
43562
- let caption;
43563
- for (const child of table.children) {
43564
- if (!isElement(child)) continue;
43565
- if (child.name === "caption") {
43566
- const capContent = this.inlineNodes(child.children, []);
43567
- const trimmed = this.trimInline(capContent);
43568
- if (trimmed.length > 0) caption = trimmed;
43569
- } else if (SECTION_TAGS.has(child.name)) {
43570
- const target = child.name === "thead" ? header : child.name === "tfoot" ? footer : body;
43571
- for (const tr of childElements(child, ROW_TAGS)) {
43572
- target.push(this.rowToCells(tr));
43573
- }
43574
- } else if (ROW_TAGS.has(child.name)) {
43575
- body.push(this.rowToCells(child));
43576
- }
43577
- }
43578
- if (header.length === 0 && body.length === 0 && footer.length === 0) {
43579
- return void 0;
43580
- }
43581
- const tableExtended = {};
43582
- if (header.length > 0) tableExtended.header = { rows: header };
43583
- if (body.length > 0) tableExtended.body = { rows: body };
43584
- if (footer.length > 0) tableExtended.footer = { rows: footer };
43585
- let tableData = tableExtended;
43586
- if (tableFormat === "table") {
43587
- if (this.isLossyAsBasic(tableExtended)) {
43588
- onWarning?.(
43589
- "Converting an HTML table to [.table] loses rowspan/colspan/scope and multi-section structure; use --tableFormat table-extended to preserve them."
43590
- );
43591
- }
43592
- tableData = convertExtendedToBasicTableFormat(tableExtended);
43593
- }
43594
- const bit = {
43595
- type: tableFormat,
43596
- format: "bitmark++",
43597
- table: tableData
43598
- };
43599
- if (caption) bit.caption = caption;
43600
- return bit;
43601
- }
43602
- rowToCells(tr) {
43603
- const cells = [];
43604
- for (const cell of childElements(tr, CELL_TAGS)) {
43605
- cells.push(this.cellToJson(cell));
43606
- }
43607
- return { cells };
43608
- }
43609
- cellToJson(cell) {
43610
- const json = {
43611
- content: this.blockNodes(cell.children)
43612
- };
43613
- if (cell.name === "th") json.title = true;
43614
- const rowspan = intAttr(cell.attrs.rowspan);
43615
- if (rowspan != null && rowspan >= 2) json.rowspan = rowspan;
43616
- const colspan = intAttr(cell.attrs.colspan);
43617
- if (colspan != null && colspan >= 2) json.colspan = colspan;
43618
- const scope = cell.attrs.scope?.toLowerCase();
43619
- if (scope && VALID_SCOPES.has(scope)) {
43620
- json.scope = scope;
43621
- }
43622
- const width = this.widthFromCell(cell);
43623
- if (width != null) json.colwidth = width;
43624
- return json;
43625
- }
43626
- widthFromCell(cell) {
43627
- const attrWidth = intAttr(cell.attrs.width);
43628
- if (attrWidth != null && attrWidth > 0) return attrWidth;
43629
- const style = cell.attrs.style;
43630
- if (style) {
43631
- const m = style.match(/width\s*:\s*(\d+)\s*px/i);
43632
- if (m) {
43633
- const n = Number.parseInt(m[1], 10);
43634
- if (Number.isInteger(n) && n > 0) return n;
43635
- }
43636
- }
43637
- return void 0;
43638
- }
43639
- isLossyAsBasic(table) {
43640
- if (table.footer) return true;
43641
- if (table.header && table.header.rows.length > 1) return true;
43642
- const sections = [table.header, table.body, table.footer];
43643
- for (const section of sections) {
43644
- if (!section) continue;
43645
- for (const row of section.rows) {
43646
- for (const cell of row.cells) {
43647
- if (cell.rowspan || cell.colspan || cell.scope) return true;
43648
- }
43649
- }
43650
- }
43651
- return false;
43652
- }
43653
- // --- content conversion (HTML -> TextAst) ---------------------------------
43654
- /**
43655
- * Convert a cell/li's child nodes into block-level TextAst nodes (paragraphs, lists, images).
43656
- */
43657
- blockNodes(nodes) {
43658
- const blocks = [];
43659
- let inline = [];
43660
- const flush = () => {
43661
- const trimmed = this.trimInline(inline);
43662
- inline = [];
43663
- if (trimmed.length === 0) return;
43664
- if (trimmed.length === 1 && trimmed[0].type === "imageInline") {
43665
- blocks.push({ ...trimmed[0], type: "image" });
43666
- } else {
43667
- blocks.push({ type: "paragraph", content: trimmed, attrs: {} });
43668
- }
43669
- };
43670
- for (const node of nodes) {
43671
- if (!isElement(node)) {
43672
- inline.push(...this.inlineText(node.value, []));
43673
- continue;
43674
- }
43675
- const name = node.name;
43676
- if (name === "p") {
43677
- flush();
43678
- const content = this.trimInline(this.inlineNodes(node.children, []));
43679
- if (content.length > 0) {
43680
- blocks.push({ type: "paragraph", content, attrs: {} });
43681
- }
43682
- } else if (this.isListTag(name)) {
43683
- flush();
43684
- const list = this.listNode(node);
43685
- if (list) blocks.push(list);
43686
- } else {
43687
- inline.push(...this.inlineFromElement(node, []));
43688
- }
43689
- }
43690
- flush();
43691
- return blocks;
43692
- }
43693
- /** Convert child nodes to a flat list of inline TextNodes, applying the given marks. */
43694
- inlineNodes(nodes, marks) {
43695
- const out = [];
43696
- for (const node of nodes) {
43697
- if (!isElement(node)) {
43698
- out.push(...this.inlineText(node.value, marks));
43699
- } else {
43700
- out.push(...this.inlineFromElement(node, marks));
43701
- }
43702
- }
43703
- return out;
43704
- }
43705
- inlineFromElement(el, marks) {
43706
- const name = el.name;
43707
- if (name === "br") {
43708
- return [{ type: "hardBreak" }];
43709
- }
43710
- if (name === "img") {
43711
- return [this.imageNode(el, true)];
43712
- }
43713
- const markType = MARK_TAGS[name];
43714
- if (markType) {
43715
- return this.inlineNodes(el.children, [...marks, { type: markType }]);
43716
- }
43717
- if (name === "a") {
43718
- const linkMark = {
43719
- type: "link",
43720
- attrs: {
43721
- href: el.attrs.href ?? "",
43722
- ...el.attrs.target ? { target: el.attrs.target } : {}
43723
- }
43724
- };
43725
- return this.inlineNodes(el.children, [...marks, linkMark]);
43726
- }
43727
- if (this.keepUnknownTags) {
43728
- const open = this.serializeOpenTag(el);
43729
- const close = `</${name}>`;
43730
- return [
43731
- ...this.inlineText(open, marks),
43732
- ...this.inlineNodes(el.children, marks),
43733
- ...this.inlineText(close, marks)
43734
- ];
43735
- }
43736
- return this.inlineNodes(el.children, marks);
43737
- }
43738
- inlineText(raw, marks) {
43739
- const text = decodeEntities(raw).replace(/\s+/g, " ");
43740
- if (text === "") return [];
43741
- const node = { text, type: "text" };
43742
- if (marks.length > 0) node.marks = marks.map((m) => ({ ...m }));
43743
- return [node];
43744
- }
43745
- serializeOpenTag(el) {
43746
- const attrs = Object.entries(el.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${v}"`).join("");
43747
- return `<${el.name}${attrs}>`;
43748
- }
43749
- // --- lists ----------------------------------------------------------------
43750
- isListTag(name) {
43751
- return name === "ul" || name === "ol";
43752
- }
43753
- listNode(el) {
43754
- const items = childElements(el, LI_TAGS);
43755
- if (items.length === 0) return void 0;
43756
- const isTask = el.name === "ul" && items.some((li) => {
43757
- const firstEl = li.children.find(isElement);
43758
- return firstEl?.name === "input" && firstEl.attrs.type?.toLowerCase() === "checkbox";
43759
- });
43760
- if (isTask) {
43761
- return {
43762
- type: "taskList",
43763
- content: items.map((li) => this.taskItemNode(li))
43764
- };
43765
- }
43766
- const type = this.listType(el);
43767
- const node = {
43768
- type,
43769
- content: items.map((li) => this.listItemNode(li))
43770
- };
43771
- if (el.name === "ol") {
43772
- const start = intAttr(el.attrs.start);
43773
- if (start != null) {
43774
- node.attrs = { start };
43775
- }
43776
- }
43777
- return node;
43778
- }
43779
- listType(el) {
43780
- if (el.name === "ol") {
43781
- switch (el.attrs.type) {
43782
- case "i":
43783
- return "orderedListRomanLower";
43784
- case "I":
43785
- return "orderedListRoman";
43786
- case "a":
43787
- return "letteredListLower";
43788
- case "A":
43789
- return "letteredList";
43790
- default:
43791
- return "orderedList";
43792
- }
43793
- }
43794
- const style = el.attrs.style ?? "";
43795
- if (/list-style(-type)?\s*:\s*none/i.test(style)) return "noBulletList";
43796
- return "bulletList";
43797
- }
43798
- listItemNode(li) {
43799
- let content = this.blockNodes(li.children);
43800
- if (content.length === 0) {
43801
- content = [{ type: "paragraph", content: [], attrs: {} }];
43802
- }
43803
- return { type: "listItem", content };
43804
- }
43805
- taskItemNode(li) {
43806
- const checkbox = li.children.find(
43807
- (c) => isElement(c) && c.name === "input" && c.attrs.type?.toLowerCase() === "checkbox"
43808
- );
43809
- const checked = checkbox ? "checked" in checkbox.attrs : false;
43810
- const rest = li.children.filter((c) => c !== checkbox);
43811
- let content = this.blockNodes(rest);
43812
- if (content.length === 0) {
43813
- content = [{ type: "paragraph", content: [], attrs: {} }];
43814
- }
43815
- return { type: "taskItem", attrs: { checked }, content };
43816
- }
43817
- // --- images ---------------------------------------------------------------
43818
- imageNode(el, inline) {
43819
- const attrs = { src: el.attrs.src ?? "" };
43820
- attrs.alt = el.attrs.alt ?? null;
43821
- attrs.title = el.attrs.title ?? null;
43822
- const width = intAttr(el.attrs.width);
43823
- if (width != null) attrs.width = width;
43824
- const height = intAttr(el.attrs.height);
43825
- if (height != null) attrs.height = height;
43826
- if (el.attrs.class) attrs.class = el.attrs.class;
43827
- return { type: inline ? "imageInline" : "image", attrs };
43828
- }
43829
- // --- whitespace -----------------------------------------------------------
43830
- /** Trim leading/trailing whitespace from inline content (edges only). */
43831
- trimInline(nodes) {
43832
- const out = nodes.slice();
43833
- const isWs = (n) => n.type === "text" && /^\s*$/.test(n.text ?? "");
43834
- while (out.length > 0 && isWs(out[0])) out.shift();
43835
- if (out.length > 0 && out[0].type === "text") {
43836
- out[0] = { ...out[0], text: (out[0].text ?? "").replace(/^\s+/, "") };
43837
- }
43838
- while (out.length > 0 && isWs(out[out.length - 1])) out.pop();
43839
- if (out.length > 0 && out[out.length - 1].type === "text") {
43840
- const last = out.length - 1;
43841
- out[last] = { ...out[last], text: (out[last].text ?? "").replace(/\s+$/, "") };
43842
- }
43843
- return out.filter((n) => !(n.type === "text" && n.text === ""));
43844
- }
43845
- };
43846
-
43847
44112
  // src/BitmarkParserGenerator.ts
43848
44113
  var Input = {
43849
44114
  /**