@gmb/bitmark-parser-generator 5.26.0 → 5.28.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.
@@ -34,8 +34,8 @@ __export(index_exports, {
34
34
  BoundingBoxIndex: () => BoundingBoxIndex,
35
35
  Builder: () => Builder,
36
36
  CardSetVersion: () => CardSetVersion,
37
- HtmlTableGenerator: () => HtmlTableGenerator2,
38
- HtmlTableParser: () => HtmlTableParser2,
37
+ HtmlTableGenerator: () => HtmlTableGenerator,
38
+ HtmlTableParser: () => HtmlTableParser,
39
39
  InfoFormat: () => InfoFormat,
40
40
  InfoType: () => InfoType,
41
41
  Input: () => Input,
@@ -596,6 +596,14 @@ var BitType2 = {
596
596
  smartStandardRemarkNonNormativeCollapsible: "smart-standard-remark-non-normative-collapsible",
597
597
  smartStandardRemarkNormative: "smart-standard-remark-normative",
598
598
  smartStandardRemarkNormativeCollapsible: "smart-standard-remark-normative-collapsible",
599
+ smartStandardRemarkTableExtendedImageNonNormative: "smart-standard-remark-table-extended-image-non-normative",
600
+ smartStandardRemarkTableExtendedImageNonNormativeCollapsible: "smart-standard-remark-table-extended-image-non-normative-collapsible",
601
+ smartStandardRemarkTableExtendedImageNormative: "smart-standard-remark-table-extended-image-normative",
602
+ smartStandardRemarkTableExtendedImageNormativeCollapsible: "smart-standard-remark-table-extended-image-normative-collapsible",
603
+ smartStandardRemarkTableExtendedNonNormative: "smart-standard-remark-table-extended-non-normative",
604
+ smartStandardRemarkTableExtendedNonNormativeCollapsible: "smart-standard-remark-table-extended-non-normative-collapsible",
605
+ smartStandardRemarkTableExtendedNormative: "smart-standard-remark-table-extended-normative",
606
+ smartStandardRemarkTableExtendedNormativeCollapsible: "smart-standard-remark-table-extended-normative-collapsible",
599
607
  smartStandardRemarkTableImageNonNormative: "smart-standard-remark-table-image-non-normative",
600
608
  smartStandardRemarkTableImageNonNormativeCollapsible: "smart-standard-remark-table-image-non-normative-collapsible",
601
609
  smartStandardRemarkTableImageNormative: "smart-standard-remark-table-image-normative",
@@ -604,6 +612,14 @@ var BitType2 = {
604
612
  smartStandardRemarkTableNonNormativeCollapsible: "smart-standard-remark-table-non-normative-collapsible",
605
613
  smartStandardRemarkTableNormative: "smart-standard-remark-table-normative",
606
614
  smartStandardRemarkTableNormativeCollapsible: "smart-standard-remark-table-normative-collapsible",
615
+ smartStandardTableExtendedImageNonNormative: "smart-standard-table-extended-image-non-normative",
616
+ smartStandardTableExtendedImageNonNormativeCollapsible: "smart-standard-table-extended-image-non-normative-collapsible",
617
+ smartStandardTableExtendedImageNormative: "smart-standard-table-extended-image-normative",
618
+ smartStandardTableExtendedImageNormativeCollapsible: "smart-standard-table-extended-image-normative-collapsible",
619
+ smartStandardTableExtendedNonNormative: "smart-standard-table-extended-non-normative",
620
+ smartStandardTableExtendedNonNormativeCollapsible: "smart-standard-table-extended-non-normative-collapsible",
621
+ smartStandardTableExtendedNormative: "smart-standard-table-extended-normative",
622
+ smartStandardTableExtendedNormativeCollapsible: "smart-standard-table-extended-normative-collapsible",
607
623
  smartStandardTableImageNonNormative: "smart-standard-table-image-non-normative",
608
624
  smartStandardTableImageNonNormativeCollapsible: "smart-standard-table-image-non-normative-collapsible",
609
625
  smartStandardTableImageNormative: "smart-standard-table-image-normative",
@@ -626,10 +642,18 @@ var BitType2 = {
626
642
  standardNoteNormative: "standard-note-normative",
627
643
  standardRemarkNonNormative: "standard-remark-non-normative",
628
644
  standardRemarkNormative: "standard-remark-normative",
645
+ standardRemarkTableExtendedImageNonNormative: "standard-remark-table-extended-image-non-normative",
646
+ standardRemarkTableExtendedImageNormative: "standard-remark-table-extended-image-normative",
647
+ standardRemarkTableExtendedNonNormative: "standard-remark-table-extended-non-normative",
648
+ standardRemarkTableExtendedNormative: "standard-remark-table-extended-normative",
629
649
  standardRemarkTableImageNonNormative: "standard-remark-table-image-non-normative",
630
650
  standardRemarkTableImageNormative: "standard-remark-table-image-normative",
631
651
  standardRemarkTableNonNormative: "standard-remark-table-non-normative",
632
652
  standardRemarkTableNormative: "standard-remark-table-normative",
653
+ standardTableExtendedImageNonNormative: "standard-table-extended-image-non-normative",
654
+ standardTableExtendedImageNormative: "standard-table-extended-image-normative",
655
+ standardTableExtendedNonNormative: "standard-table-extended-non-normative",
656
+ standardTableExtendedNormative: "standard-table-extended-normative",
633
657
  standardTableImageNonNormative: "standard-table-image-non-normative",
634
658
  standardTableImageNormative: "standard-table-image-normative",
635
659
  standardTableNonNormative: "standard-table-non-normative",
@@ -856,6 +880,10 @@ var AbstractTagConfig = class {
856
880
  // New JSON-pattern jsonKey (export-only)
857
881
  __publicField(this, "hasExportJsonKey");
858
882
  // True iff exportJsonKey was explicitly set
883
+ __publicField(this, "htmlKey");
884
+ // HTML key-pattern (export-only)
885
+ __publicField(this, "hasHtmlKey");
886
+ // True iff htmlKey was explicitly set
859
887
  __publicField(this, "deprecated");
860
888
  this.type = params.type;
861
889
  this.configKey = params.configKey;
@@ -866,6 +894,8 @@ var AbstractTagConfig = class {
866
894
  this.jsonKey = params.jsonKey;
867
895
  this.exportJsonKey = params.exportJsonKey;
868
896
  this.hasExportJsonKey = params.hasExportJsonKey;
897
+ this.htmlKey = params.htmlKey;
898
+ this.hasHtmlKey = params.hasHtmlKey;
869
899
  this.deprecated = params.deprecated;
870
900
  }
871
901
  };
@@ -987,7 +1017,7 @@ var ResourceTagConfig = class extends AbstractTagConfig {
987
1017
  // src/model/config/CardVariantConfig.ts
988
1018
  var CardVariantConfig = class {
989
1019
  // Body text format. Default: bitmark++
990
- constructor(tags2, bodyAllowed, bodyRequired, repeatCount, jsonKey, exportJsonKey, hasExportJsonKey = false, format) {
1020
+ constructor(tags2, bodyAllowed, bodyRequired, repeatCount, jsonKey, exportJsonKey, hasExportJsonKey = false, format, htmlKey, hasHtmlKey = false) {
991
1021
  __publicField(this, "tags");
992
1022
  __publicField(this, "bodyAllowed");
993
1023
  // Default: true
@@ -1001,6 +1031,9 @@ var CardVariantConfig = class {
1001
1031
  __publicField(this, "exportJsonKey");
1002
1032
  // New JSON-pattern jsonKey
1003
1033
  __publicField(this, "hasExportJsonKey");
1034
+ __publicField(this, "htmlKey");
1035
+ // HTML key-pattern
1036
+ __publicField(this, "hasHtmlKey");
1004
1037
  __publicField(this, "format");
1005
1038
  this.tags = tags2;
1006
1039
  this.bodyAllowed = bodyAllowed == null ? true : bodyAllowed;
@@ -1009,6 +1042,8 @@ var CardVariantConfig = class {
1009
1042
  this.jsonKey = jsonKey;
1010
1043
  this.exportJsonKey = exportJsonKey;
1011
1044
  this.hasExportJsonKey = hasExportJsonKey;
1045
+ this.htmlKey = htmlKey;
1046
+ this.hasHtmlKey = hasHtmlKey;
1012
1047
  this.format = format;
1013
1048
  }
1014
1049
  toString(options) {
@@ -1033,18 +1068,22 @@ ${tag.toString(opts)}`;
1033
1068
 
1034
1069
  // src/model/config/CardSideConfig.ts
1035
1070
  var CardSideConfig = class {
1036
- constructor(name, repeat, variants, jsonKey, exportJsonKey, hasExportJsonKey = false) {
1071
+ constructor(name, repeat, variants, jsonKey, exportJsonKey, hasExportJsonKey = false, htmlKey, hasHtmlKey = false) {
1037
1072
  __publicField(this, "name");
1038
1073
  __publicField(this, "repeat");
1039
1074
  __publicField(this, "jsonKey");
1040
1075
  __publicField(this, "exportJsonKey");
1041
1076
  __publicField(this, "hasExportJsonKey");
1077
+ __publicField(this, "htmlKey");
1078
+ __publicField(this, "hasHtmlKey");
1042
1079
  __publicField(this, "variants");
1043
1080
  this.name = name;
1044
1081
  this.repeat = repeat;
1045
1082
  this.jsonKey = jsonKey;
1046
1083
  this.exportJsonKey = exportJsonKey;
1047
1084
  this.hasExportJsonKey = hasExportJsonKey;
1085
+ this.htmlKey = htmlKey;
1086
+ this.hasHtmlKey = hasHtmlKey;
1048
1087
  this.variants = variants;
1049
1088
  }
1050
1089
  toString(options) {
@@ -1066,17 +1105,21 @@ var CardSideConfig = class {
1066
1105
 
1067
1106
  // src/model/config/CardSetConfig.ts
1068
1107
  var CardSetConfig = class {
1069
- constructor(configKey, jsonKey, exportJsonKey, hasExportJsonKey, sections, sides) {
1108
+ constructor(configKey, jsonKey, exportJsonKey, hasExportJsonKey, sections, sides, htmlKey = void 0, hasHtmlKey = false) {
1070
1109
  __publicField(this, "configKey");
1071
1110
  __publicField(this, "jsonKey");
1072
1111
  __publicField(this, "exportJsonKey");
1073
1112
  __publicField(this, "hasExportJsonKey");
1113
+ __publicField(this, "htmlKey");
1114
+ __publicField(this, "hasHtmlKey");
1074
1115
  __publicField(this, "sections");
1075
1116
  __publicField(this, "sides");
1076
1117
  this.configKey = configKey;
1077
1118
  this.jsonKey = jsonKey;
1078
1119
  this.exportJsonKey = exportJsonKey;
1079
1120
  this.hasExportJsonKey = hasExportJsonKey;
1121
+ this.htmlKey = htmlKey;
1122
+ this.hasHtmlKey = hasHtmlKey;
1080
1123
  this.sections = sections;
1081
1124
  this.sides = sides;
1082
1125
  }
@@ -1373,6 +1416,8 @@ var propertyKeys = {
1373
1416
  property_externalLinkText: "@externalLinkText",
1374
1417
  property_feedbackEngine: "@feedbackEngine",
1375
1418
  property_feedbackType: "@feedbackType",
1419
+ property_userFeedbackTranslationUrl: "@userFeedbackTranslationUrl",
1420
+ property_userFeedbackContentUrl: "@userFeedbackContentUrl",
1376
1421
  property_flashcardSet: "@flashcardSet",
1377
1422
  property_focusX: "@focusX",
1378
1423
  property_focusY: "@focusY",
@@ -3756,6 +3801,16 @@ var CARDSETS = {
3756
3801
  [CardSetConfigKey.table]: {
3757
3802
  jsonKey: null,
3758
3803
  exportJsonKey: null,
3804
+ // PLAN-109 / HTML.md §8 — idiomatic HTML mapping. The classic (non-extended)
3805
+ // table produces the same `<table>`/`<thead>`/`<tbody>`/`<tfoot>` structure as
3806
+ // table-extended, so the structural htmlKeys mirror that cardSet. NOTE: the
3807
+ // JSON side routes the FIRST header card → `columns` (header) and SUBSEQUENT
3808
+ // header cards → `data` (body); HTML uses a uniform `<thead>` for the whole
3809
+ // `table-header` section, so a multi-card header (e.g. a second header card
3810
+ // that JSON sends to `data`) renders as additional `<thead>` rows rather than
3811
+ // moving to `<tbody>`. Per-card header/body splitting needs engine-level
3812
+ // routing not yet expressible in the pattern vocabulary — see PLAN-109.
3813
+ htmlKey: { "@el": "table", "@children": "$" },
3759
3814
  sections: {
3760
3815
  "table-header": {
3761
3816
  // First header card's row of cells → `columns`. Subsequent header
@@ -3774,25 +3829,36 @@ var CARDSETS = {
3774
3829
  },
3775
3830
  { "@cardIndex=0": { "@bit": { table: { columns: "$" } } } },
3776
3831
  { "@bit": { table: { data: ["$"] } } }
3777
- ]
3832
+ ],
3833
+ // Header section → `<thead>` rows of `<th>`; `@tableColWidth` bubbles a
3834
+ // `width` attr onto the enclosing `<th>` (HTML-C2).
3835
+ htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
3836
+ sideHtmlKey: { "@el": "th", "@children": "$" }
3778
3837
  },
3779
3838
  "table-body": {
3780
3839
  // Batched emission: `$` is the entire 2D array of body card rows.
3781
3840
  // This is the default section (no qualifier → cards land here).
3782
3841
  jsonKey: "table.data",
3783
3842
  exportJsonKey: { "@bit": { table: { data: "$" } } },
3843
+ htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
3784
3844
  isDefault: true
3785
3845
  },
3786
3846
  "table-footer": {
3787
3847
  // Batched emission: `$` is the entire 2D array of footer rows.
3788
3848
  jsonKey: "table.data",
3789
- exportJsonKey: { "@bit": { table: { data: "$" } } }
3849
+ exportJsonKey: { "@bit": { table: { data: "$" } } },
3850
+ // Footer cells route to `data` (not `columns`), so they are `<td>` (no
3851
+ // sideHtmlKey override — the cell side default applies).
3852
+ htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } }
3790
3853
  }
3791
3854
  },
3792
3855
  sides: [
3793
3856
  {
3794
3857
  name: "cell",
3795
3858
  repeat: true,
3859
+ // Default (body/footer) cell → `<td>`; the header section overrides to
3860
+ // `<th>` via its sideHtmlKey (HTML.md §8).
3861
+ htmlKey: { "@el": "td", "@children": "$" },
3796
3862
  variants: [
3797
3863
  {
3798
3864
  jsonKey: null,
@@ -3809,21 +3875,32 @@ var CARDSETS = {
3809
3875
  },
3810
3876
  {
3811
3877
  key: ConfigKey.property_tableCellType,
3878
+ // HTML.md §8: first-match predicate selecting `<th>` vs `<td>`.
3879
+ htmlKey: [{ "@value=th": { "@el": "th" } }, { "@absent": { "@el": "td" } }],
3812
3880
  description: "Table cell type (th/td).",
3813
3881
  format: TagFormat.plainText
3814
3882
  },
3815
3883
  {
3816
3884
  key: ConfigKey.property_tableRowSpan,
3885
+ // HTML-C2: contribute a `rowspan` attribute onto the enclosing
3886
+ // cell element (`<th>`/`<td>`).
3887
+ htmlKey: { "@el": { "@attr": { rowspan: "$" } } },
3817
3888
  description: "Number of rows the cell spans.",
3818
3889
  format: TagFormat.number
3819
3890
  },
3820
3891
  {
3821
3892
  key: ConfigKey.property_tableColSpan,
3893
+ // HTML-C2: contribute a `colspan` attribute onto the enclosing
3894
+ // cell element (`<th>`/`<td>`).
3895
+ htmlKey: { "@el": { "@attr": { colspan: "$" } } },
3822
3896
  description: "Number of columns the cell spans.",
3823
3897
  format: TagFormat.number
3824
3898
  },
3825
3899
  {
3826
3900
  key: ConfigKey.property_tableScope,
3901
+ // HTML-C2: contribute a `scope` attribute onto the enclosing
3902
+ // cell element (`<th>`/`<td>`).
3903
+ htmlKey: { "@el": { "@attr": { scope: "$" } } },
3827
3904
  description: "Scope attribute for header cells.",
3828
3905
  format: TagFormat.plainText
3829
3906
  },
@@ -3831,6 +3908,9 @@ var CARDSETS = {
3831
3908
  key: ConfigKey.property_tableColWidth,
3832
3909
  jsonKey: "^table.columnWidths[{s}]",
3833
3910
  exportJsonKey: { "@bit": { table: { columnWidths: { $s: "$" } } } },
3911
+ // HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
3912
+ // enclosing cell element (`<th>`/`<td>`).
3913
+ htmlKey: { "@el": { "@attr": { width: "$" } } },
3834
3914
  description: "Width for the column.",
3835
3915
  format: TagFormat.number
3836
3916
  }
@@ -3846,6 +3926,12 @@ var CARDSETS = {
3846
3926
  //
3847
3927
  [CardSetConfigKey.tableExtended]: {
3848
3928
  jsonKey: null,
3929
+ // PLAN-109 / HTML.md §8 — idiomatic HTML mapping for the extended table.
3930
+ // The card-set container is the `<table>` element; each section is a
3931
+ // table section element (`<thead>`/`<tbody>`/`<tfoot>`) whose children are
3932
+ // `<tr>` rows, and each cell side is a `<td>` (body) or `<th>` (header /
3933
+ // footer, via the section sideHtmlKey override).
3934
+ htmlKey: { "@el": "table", "@children": "$" },
3849
3935
  sections: {
3850
3936
  "table-header": {
3851
3937
  jsonKey: "table.header.rows",
@@ -3865,19 +3951,29 @@ var CARDSETS = {
3865
3951
  },
3866
3952
  { table: { header: { rows: ["$"] } } }
3867
3953
  ],
3954
+ htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
3868
3955
  sideJsonKey: "cells[{s}]|set(title=true)",
3869
- sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } }
3956
+ sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
3957
+ // Header cells are `<th>`. The `width` attribute is contributed by the
3958
+ // `@tableColWidth` tag, which bubbles it onto the enclosing `<th>`
3959
+ // (HTML-C2) — so it is not repeated here.
3960
+ sideHtmlKey: { "@el": "th", "@children": "$" }
3870
3961
  },
3871
3962
  "table-body": {
3872
3963
  jsonKey: "table.body.rows",
3873
3964
  exportJsonKey: { table: { body: { rows: "$" } } },
3965
+ htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
3874
3966
  isDefault: true
3875
3967
  },
3876
3968
  "table-footer": {
3877
3969
  jsonKey: "table.footer.rows",
3878
3970
  exportJsonKey: { table: { footer: { rows: "$" } } },
3971
+ htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } },
3879
3972
  sideJsonKey: "cells[{s}]|set(title=true)",
3880
- sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } }
3973
+ sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
3974
+ // Footer cells render as `<th>` (mirrors the header `title: true`
3975
+ // mapping); §8 lists only the header side, so this parallels it.
3976
+ sideHtmlKey: { "@el": "th", "@children": "$" }
3881
3977
  }
3882
3978
  },
3883
3979
  sides: [
@@ -3886,6 +3982,9 @@ var CARDSETS = {
3886
3982
  repeat: true,
3887
3983
  jsonKey: "cells[{s}]",
3888
3984
  exportJsonKey: { cells: { $s: "$" } },
3985
+ // Default (body) cell → `<td>`; the header/footer sections override the
3986
+ // side to `<th>` via their sideHtmlKey (HTML.md §8).
3987
+ htmlKey: { "@el": "td", "@children": "$" },
3889
3988
  variants: [
3890
3989
  {
3891
3990
  jsonKey: "content",
@@ -3909,6 +4008,9 @@ var CARDSETS = {
3909
4008
  key: ConfigKey.property_tableCellType,
3910
4009
  jsonKey: "title|bool(th)",
3911
4010
  exportJsonKey: [{ "@value=th": { title: true } }],
4011
+ // HTML.md §8: same first-match predicate machinery selecting
4012
+ // `<th>` vs `<td>` instead of `title:true` vs nothing.
4013
+ htmlKey: [{ "@value=th": { "@el": "th" } }, { "@absent": { "@el": "td" } }],
3912
4014
  description: "Table cell type (th/td).",
3913
4015
  format: TagFormat.plainText
3914
4016
  },
@@ -3916,6 +4018,9 @@ var CARDSETS = {
3916
4018
  key: ConfigKey.property_tableRowSpan,
3917
4019
  jsonKey: "rowspan",
3918
4020
  exportJsonKey: { rowspan: "$" },
4021
+ // HTML-C2: contribute a `rowspan` attribute onto the enclosing
4022
+ // cell element (`<th>`/`<td>`).
4023
+ htmlKey: { "@el": { "@attr": { rowspan: "$" } } },
3919
4024
  description: "Number of rows the cell spans.",
3920
4025
  format: TagFormat.number
3921
4026
  },
@@ -3923,6 +4028,9 @@ var CARDSETS = {
3923
4028
  key: ConfigKey.property_tableColSpan,
3924
4029
  jsonKey: "colspan",
3925
4030
  exportJsonKey: { colspan: "$" },
4031
+ // HTML-C2: contribute a `colspan` attribute onto the enclosing
4032
+ // cell element (`<th>`/`<td>`).
4033
+ htmlKey: { "@el": { "@attr": { colspan: "$" } } },
3926
4034
  description: "Number of columns the cell spans.",
3927
4035
  format: TagFormat.number
3928
4036
  },
@@ -3930,6 +4038,9 @@ var CARDSETS = {
3930
4038
  key: ConfigKey.property_tableScope,
3931
4039
  jsonKey: "scope",
3932
4040
  exportJsonKey: { scope: "$" },
4041
+ // HTML-C2: contribute a `scope` attribute onto the enclosing
4042
+ // cell element (`<th>`/`<td>`).
4043
+ htmlKey: { "@el": { "@attr": { scope: "$" } } },
3933
4044
  description: "Scope attribute for header cells.",
3934
4045
  format: TagFormat.plainText
3935
4046
  },
@@ -3938,6 +4049,9 @@ var CARDSETS = {
3938
4049
  // PLAN-080: write to the cell's `colwidth` field.
3939
4050
  jsonKey: "colwidth",
3940
4051
  exportJsonKey: { colwidth: "$" },
4052
+ // HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
4053
+ // enclosing cell element (`<th>`/`<td>`).
4054
+ htmlKey: { "@el": { "@attr": { width: "$" } } },
3941
4055
  description: "Width for the column.",
3942
4056
  format: TagFormat.number
3943
4057
  }
@@ -5737,6 +5851,16 @@ var GROUPS = {
5737
5851
  description: "External Id of the translated book",
5738
5852
  format: TagFormat.plainText
5739
5853
  },
5854
+ {
5855
+ key: ConfigKey.property_userFeedbackTranslationUrl,
5856
+ description: "URL to an external feedback form for book translation feedback",
5857
+ format: TagFormat.plainText
5858
+ },
5859
+ {
5860
+ key: ConfigKey.property_userFeedbackContentUrl,
5861
+ description: "URL to an external feedback form for book content feedback",
5862
+ format: TagFormat.plainText
5863
+ },
5740
5864
  {
5741
5865
  key: ConfigKey.property_duration,
5742
5866
  description: "The duration of the book",
@@ -6866,7 +6990,9 @@ var ConfigHydrator = class {
6866
6990
  variantsOfSide,
6867
6991
  _side.jsonKey,
6868
6992
  _side.exportJsonKey,
6869
- Object.prototype.hasOwnProperty.call(_side, "exportJsonKey")
6993
+ Object.prototype.hasOwnProperty.call(_side, "exportJsonKey"),
6994
+ _side.htmlKey,
6995
+ Object.prototype.hasOwnProperty.call(_side, "htmlKey")
6870
6996
  );
6871
6997
  sides.push(sideConfig);
6872
6998
  }
@@ -6876,7 +7002,9 @@ var ConfigHydrator = class {
6876
7002
  _cardSetConfig.exportJsonKey,
6877
7003
  Object.prototype.hasOwnProperty.call(_cardSetConfig, "exportJsonKey"),
6878
7004
  this.hydrateCardSections(_cardSetConfig.sections),
6879
- sides
7005
+ sides,
7006
+ _cardSetConfig.htmlKey,
7007
+ Object.prototype.hasOwnProperty.call(_cardSetConfig, "htmlKey")
6880
7008
  );
6881
7009
  return cardSetConfig;
6882
7010
  }
@@ -6888,10 +7016,14 @@ var ConfigHydrator = class {
6888
7016
  jsonKey: v.jsonKey,
6889
7017
  exportJsonKey: v.exportJsonKey,
6890
7018
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(v, "exportJsonKey"),
7019
+ htmlKey: v.htmlKey,
7020
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(v, "htmlKey"),
6891
7021
  isDefault: v.isDefault,
6892
7022
  sideJsonKey: v.sideJsonKey,
6893
7023
  sideExportJsonKey: v.sideExportJsonKey,
6894
7024
  hasSideExportJsonKey: Object.prototype.hasOwnProperty.call(v, "sideExportJsonKey"),
7025
+ sideHtmlKey: v.sideHtmlKey,
7026
+ hasSideHtmlKey: Object.prototype.hasOwnProperty.call(v, "sideHtmlKey"),
6895
7027
  // PLAN-085: cardinality fields pass through; defaults applied
6896
7028
  // downstream by the config consumer (Rust treats `0` / undefined
6897
7029
  // as unbounded).
@@ -6909,7 +7041,8 @@ var ConfigHydrator = class {
6909
7041
  chain: _chain,
6910
7042
  deprecated,
6911
7043
  jsonKey,
6912
- exportJsonKey
7044
+ exportJsonKey,
7045
+ htmlKey
6913
7046
  } = _tag;
6914
7047
  const configKey = (0, import_superenum15.Enum)(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
6915
7048
  if (!configKey) throw new Error(`No tag key found for config key '${configKey}'`);
@@ -6928,6 +7061,8 @@ var ConfigHydrator = class {
6928
7061
  jsonKey,
6929
7062
  exportJsonKey,
6930
7063
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
7064
+ htmlKey,
7065
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
6931
7066
  deprecated
6932
7067
  });
6933
7068
  return {
@@ -6947,7 +7082,8 @@ var ConfigHydrator = class {
6947
7082
  values,
6948
7083
  defaultValue,
6949
7084
  jsonKey,
6950
- exportJsonKey
7085
+ exportJsonKey,
7086
+ htmlKey
6951
7087
  } = _tag;
6952
7088
  const configKey = (0, import_superenum15.Enum)(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
6953
7089
  if (!configKey) throw new Error(`No property key found for config key '${configKey}'`);
@@ -6965,6 +7101,8 @@ var ConfigHydrator = class {
6965
7101
  jsonKey,
6966
7102
  exportJsonKey,
6967
7103
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
7104
+ htmlKey,
7105
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
6968
7106
  format,
6969
7107
  values,
6970
7108
  defaultValue,
@@ -6984,7 +7122,8 @@ var ConfigHydrator = class {
6984
7122
  chain: _chain,
6985
7123
  deprecated,
6986
7124
  jsonKey,
6987
- exportJsonKey
7125
+ exportJsonKey,
7126
+ htmlKey
6988
7127
  } = _tag;
6989
7128
  const configKey = (0, import_superenum15.Enum)(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
6990
7129
  if (!configKey) throw new Error(`No resource key found for config key '${configKey}'`);
@@ -7002,6 +7141,8 @@ var ConfigHydrator = class {
7002
7141
  jsonKey,
7003
7142
  exportJsonKey,
7004
7143
  hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
7144
+ htmlKey,
7145
+ hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
7005
7146
  deprecated
7006
7147
  });
7007
7148
  return {
@@ -7040,6 +7181,7 @@ var ConfigHydrator = class {
7040
7181
  repeatCount,
7041
7182
  jsonKey,
7042
7183
  exportJsonKey,
7184
+ htmlKey,
7043
7185
  format
7044
7186
  } = _variant;
7045
7187
  const tags2 = this.hydrateTagsConfig(_tags);
@@ -7051,7 +7193,9 @@ var ConfigHydrator = class {
7051
7193
  jsonKey,
7052
7194
  exportJsonKey,
7053
7195
  Object.prototype.hasOwnProperty.call(_variant, "exportJsonKey"),
7054
- format
7196
+ format,
7197
+ htmlKey,
7198
+ Object.prototype.hasOwnProperty.call(_variant, "htmlKey")
7055
7199
  );
7056
7200
  return cardSetConfig;
7057
7201
  }
@@ -10184,11 +10328,6 @@ var BITS = {
10184
10328
  baseBitType: BitType2._standard,
10185
10329
  description: "Extractor rule bit, used to define extraction rules with reference images and instructions",
10186
10330
  tags: [
10187
- {
10188
- exportJsonKey: { title: "$" },
10189
- key: ConfigKey.tag_title,
10190
- description: "The name of the extraction rule"
10191
- },
10192
10331
  {
10193
10332
  key: ConfigKey.group_resourceBitTags,
10194
10333
  description: "Resource bit tags for extraction rule images"
@@ -10824,7 +10963,9 @@ var BITS = {
10824
10963
  {
10825
10964
  key: ConfigKey.property_caption,
10826
10965
  description: "Caption for the table image, used to provide a description for the image",
10827
- format: TagFormat.bitmarkText
10966
+ format: TagFormat.bitmarkText,
10967
+ // HTML-10 / HTML-C2: render as the table's `<caption>` (see table bit).
10968
+ htmlKey: { "@el": "caption", "@children": "$" }
10828
10969
  },
10829
10970
  {
10830
10971
  key: ConfigKey.group_backgroundWallpaper,
@@ -10867,6 +11008,26 @@ var BITS = {
10867
11008
  baseBitType: BitType2.tableImage,
10868
11009
  description: "Standard non-normative remark table image bit, used to create non-normative remark images in tables in articles or books"
10869
11010
  },
11011
+ [BitType2.standardTableExtendedImageNormative]: {
11012
+ since: "1.16.0",
11013
+ baseBitType: BitType2.tableExtendedImage,
11014
+ description: "Standard normative extended table image bit, used to create normative extended images in tables in articles or books"
11015
+ },
11016
+ [BitType2.standardTableExtendedImageNonNormative]: {
11017
+ since: "1.16.0",
11018
+ baseBitType: BitType2.tableExtendedImage,
11019
+ description: "Standard non-normative extended table image bit, used to create non-normative extended images in tables in articles or books"
11020
+ },
11021
+ [BitType2.standardRemarkTableExtendedImageNormative]: {
11022
+ since: "1.17.0",
11023
+ baseBitType: BitType2.tableExtendedImage,
11024
+ description: "Standard normative remark extended table image bit, used to create normative remark extended images in tables in articles or books"
11025
+ },
11026
+ [BitType2.standardRemarkTableExtendedImageNonNormative]: {
11027
+ since: "1.17.0",
11028
+ baseBitType: BitType2.tableExtendedImage,
11029
+ description: "Standard non-normative remark extended table image bit, used to create non-normative remark extended images in tables in articles or books"
11030
+ },
10870
11031
  [BitType2.smartStandardTableImageNormative]: {
10871
11032
  since: "1.28.0",
10872
11033
  baseBitType: BitType2.standardTableImageNormative,
@@ -10887,6 +11048,26 @@ var BITS = {
10887
11048
  baseBitType: BitType2.standardRemarkTableImageNonNormative,
10888
11049
  description: "Smart standard non-normative remark table image bit, used to create smart standard non-normative remark images in tables in articles or books"
10889
11050
  },
11051
+ [BitType2.smartStandardTableExtendedImageNormative]: {
11052
+ since: "1.28.0",
11053
+ baseBitType: BitType2.standardTableExtendedImageNormative,
11054
+ description: "Smart standard normative extended table image bit, used to create smart standard normative extended images in tables in articles or books"
11055
+ },
11056
+ [BitType2.smartStandardTableExtendedImageNonNormative]: {
11057
+ since: "1.28.0",
11058
+ baseBitType: BitType2.standardTableExtendedImageNonNormative,
11059
+ description: "Smart standard non-normative extended table image bit, used to create smart standard non-normative extended images in tables in articles or books"
11060
+ },
11061
+ [BitType2.smartStandardRemarkTableExtendedImageNormative]: {
11062
+ since: "1.28.0",
11063
+ baseBitType: BitType2.standardRemarkTableExtendedImageNormative,
11064
+ description: "Smart standard normative remark extended table image bit, used to create smart standard normative remark extended images in tables in articles or books"
11065
+ },
11066
+ [BitType2.smartStandardRemarkTableExtendedImageNonNormative]: {
11067
+ since: "1.28.0",
11068
+ baseBitType: BitType2.standardRemarkTableExtendedImageNonNormative,
11069
+ 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"
11070
+ },
10890
11071
  [BitType2.smartStandardTableImageNormativeCollapsible]: {
10891
11072
  since: "1.28.0",
10892
11073
  baseBitType: BitType2.smartStandardTableImageNormative,
@@ -10907,6 +11088,26 @@ var BITS = {
10907
11088
  baseBitType: BitType2.smartStandardRemarkTableImageNonNormative,
10908
11089
  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"
10909
11090
  },
11091
+ [BitType2.smartStandardTableExtendedImageNormativeCollapsible]: {
11092
+ since: "1.28.0",
11093
+ baseBitType: BitType2.smartStandardTableExtendedImageNormative,
11094
+ description: "Collapsible smart standard normative extended table image bit, used to create collapsible smart standard normative extended images in tables in articles or books"
11095
+ },
11096
+ [BitType2.smartStandardTableExtendedImageNonNormativeCollapsible]: {
11097
+ since: "1.28.0",
11098
+ baseBitType: BitType2.smartStandardTableExtendedImageNonNormative,
11099
+ 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"
11100
+ },
11101
+ [BitType2.smartStandardRemarkTableExtendedImageNormativeCollapsible]: {
11102
+ since: "1.28.0",
11103
+ baseBitType: BitType2.smartStandardRemarkTableExtendedImageNormative,
11104
+ 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"
11105
+ },
11106
+ [BitType2.smartStandardRemarkTableExtendedImageNonNormativeCollapsible]: {
11107
+ since: "1.28.0",
11108
+ baseBitType: BitType2.smartStandardRemarkTableExtendedImageNonNormative,
11109
+ 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"
11110
+ },
10910
11111
  [BitType2.imageLink]: {
10911
11112
  since: "1.3.0",
10912
11113
  baseBitType: BitType2._standard,
@@ -12455,7 +12656,12 @@ var BITS = {
12455
12656
  {
12456
12657
  key: ConfigKey.property_caption,
12457
12658
  description: "Caption for the table, used to define the title of the table",
12458
- format: TagFormat.bitmarkText
12659
+ format: TagFormat.bitmarkText,
12660
+ // HTML-10 / HTML-C2: a bit-level caption renders as the table's
12661
+ // `<caption>` — placed inside the idiomatic `<table>` container as
12662
+ // its first child. Outside a table context it falls back to the
12663
+ // residual carrier.
12664
+ htmlKey: { "@el": "caption", "@children": "$" }
12459
12665
  },
12460
12666
  {
12461
12667
  key: ConfigKey.property_tableFixedHeader,
@@ -12540,7 +12746,9 @@ var BITS = {
12540
12746
  {
12541
12747
  key: ConfigKey.property_caption,
12542
12748
  description: "Caption for the table image, used to provide a description for the image",
12543
- format: TagFormat.bitmarkText
12749
+ format: TagFormat.bitmarkText,
12750
+ // HTML-10 / HTML-C2: render as the table's `<caption>` (see table bit).
12751
+ htmlKey: { "@el": "caption", "@children": "$" }
12544
12752
  },
12545
12753
  {
12546
12754
  key: ConfigKey.group_backgroundWallpaper,
@@ -12583,6 +12791,26 @@ var BITS = {
12583
12791
  baseBitType: BitType2.table,
12584
12792
  description: "Standard non-normative remark table bit, used to create standard non-normative remark tables in articles or books"
12585
12793
  },
12794
+ [BitType2.standardTableExtendedNormative]: {
12795
+ since: "1.16.0",
12796
+ baseBitType: BitType2.tableExtended,
12797
+ description: "Standard normative extended table bit, used to create standard normative extended tables in articles or books"
12798
+ },
12799
+ [BitType2.standardTableExtendedNonNormative]: {
12800
+ since: "1.16.0",
12801
+ baseBitType: BitType2.tableExtended,
12802
+ description: "Standard non-normative extended table bit, used to create standard non-normative extended tables in articles or books"
12803
+ },
12804
+ [BitType2.standardRemarkTableExtendedNormative]: {
12805
+ since: "1.17.0",
12806
+ baseBitType: BitType2.tableExtended,
12807
+ description: "Standard normative remark extended table bit, used to create standard normative remark extended tables in articles or books"
12808
+ },
12809
+ [BitType2.standardRemarkTableExtendedNonNormative]: {
12810
+ since: "1.17.0",
12811
+ baseBitType: BitType2.tableExtended,
12812
+ description: "Standard non-normative remark extended table bit, used to create standard non-normative remark extended tables in articles or books"
12813
+ },
12586
12814
  [BitType2.smartStandardTableNormative]: {
12587
12815
  since: "1.28.0",
12588
12816
  baseBitType: BitType2.standardTableNormative,
@@ -12603,6 +12831,26 @@ var BITS = {
12603
12831
  baseBitType: BitType2.standardRemarkTableNonNormative,
12604
12832
  description: "Smart standard non-normative remark table bit, used to create smart standard non-normative remark tables in articles or books"
12605
12833
  },
12834
+ [BitType2.smartStandardTableExtendedNormative]: {
12835
+ since: "1.28.0",
12836
+ baseBitType: BitType2.standardTableExtendedNormative,
12837
+ description: "Smart standard normative extended table bit, used to create smart standard normative extended tables in articles or books"
12838
+ },
12839
+ [BitType2.smartStandardTableExtendedNonNormative]: {
12840
+ since: "1.28.0",
12841
+ baseBitType: BitType2.standardTableExtendedNonNormative,
12842
+ description: "Smart standard non-normative extended table bit, used to create smart standard non-normative extended tables in articles or books"
12843
+ },
12844
+ [BitType2.smartStandardRemarkTableExtendedNormative]: {
12845
+ since: "1.28.0",
12846
+ baseBitType: BitType2.standardRemarkTableExtendedNormative,
12847
+ description: "Smart standard normative remark extended table bit, used to create smart standard normative remark extended tables in articles or books"
12848
+ },
12849
+ [BitType2.smartStandardRemarkTableExtendedNonNormative]: {
12850
+ since: "1.28.0",
12851
+ baseBitType: BitType2.standardRemarkTableExtendedNonNormative,
12852
+ description: "Smart standard non-normative remark extended table bit, used to create smart standard non-normative remark extended tables in articles or books"
12853
+ },
12606
12854
  [BitType2.smartStandardTableNormativeCollapsible]: {
12607
12855
  since: "1.28.0",
12608
12856
  baseBitType: BitType2.smartStandardTableNormative,
@@ -12623,6 +12871,26 @@ var BITS = {
12623
12871
  baseBitType: BitType2.smartStandardRemarkTableNonNormative,
12624
12872
  description: "Collapsible smart standard non-normative remark table bit, used to create collapsible smart standard non-normative remark tables in articles or books"
12625
12873
  },
12874
+ [BitType2.smartStandardTableExtendedNormativeCollapsible]: {
12875
+ since: "1.28.0",
12876
+ baseBitType: BitType2.smartStandardTableExtendedNormative,
12877
+ description: "Collapsible smart standard normative extended table bit, used to create collapsible smart standard normative extended tables in articles or books"
12878
+ },
12879
+ [BitType2.smartStandardTableExtendedNonNormativeCollapsible]: {
12880
+ since: "1.28.0",
12881
+ baseBitType: BitType2.smartStandardTableExtendedNonNormative,
12882
+ description: "Collapsible smart standard non-normative extended table bit, used to create collapsible smart standard non-normative extended tables in articles or books"
12883
+ },
12884
+ [BitType2.smartStandardRemarkTableExtendedNormativeCollapsible]: {
12885
+ since: "1.28.0",
12886
+ baseBitType: BitType2.smartStandardRemarkTableExtendedNormative,
12887
+ description: "Collapsible smart standard normative remark extended table bit, used to create collapsible smart standard normative remark extended tables in articles or books"
12888
+ },
12889
+ [BitType2.smartStandardRemarkTableExtendedNonNormativeCollapsible]: {
12890
+ since: "1.28.0",
12891
+ baseBitType: BitType2.smartStandardRemarkTableExtendedNonNormative,
12892
+ 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"
12893
+ },
12626
12894
  [BitType2.parameters]: {
12627
12895
  since: "1.18.0",
12628
12896
  baseBitType: BitType2.table,
@@ -13564,6 +13832,8 @@ var Config = class {
13564
13832
  jsonKey: tag.jsonKey,
13565
13833
  exportJsonKey: tag.exportJsonKey,
13566
13834
  hasExportJsonKey: tag.hasExportJsonKey,
13835
+ htmlKey: tag.htmlKey,
13836
+ hasHtmlKey: tag.hasHtmlKey,
13567
13837
  deprecated: tag.deprecated
13568
13838
  });
13569
13839
  finalResourceTags[k] = newTag;
@@ -13668,7 +13938,7 @@ var instance2 = new Config();
13668
13938
  // src/generated/package_info.ts
13669
13939
  var PACKAGE_INFO = {
13670
13940
  "name": "@gmb/bitmark-parser-generator",
13671
- "version": "5.26.0",
13941
+ "version": "5.28.0",
13672
13942
  "author": "Get More Brain Ltd",
13673
13943
  "license": "ISC",
13674
13944
  "description": "A bitmark parser and generator using Peggy.js"
@@ -14558,6 +14828,10 @@ var NodeType = {
14558
14828
  relatedBookValue: "relatedBookValue",
14559
14829
  translationOfBook: "translationOfBook",
14560
14830
  translationOfBookValue: "translationOfBookValue",
14831
+ userFeedbackTranslationUrl: "userFeedbackTranslationUrl",
14832
+ userFeedbackTranslationUrlValue: "userFeedbackTranslationUrlValue",
14833
+ userFeedbackContentUrl: "userFeedbackContentUrl",
14834
+ userFeedbackContentUrlValue: "userFeedbackContentUrlValue",
14561
14835
  releaseDate: "releaseDate",
14562
14836
  releaseDateValue: "releaseDateValue",
14563
14837
  releaseKind: "releaseKind",
@@ -27767,6 +28041,18 @@ var Builder = class extends BaseBuilder {
27767
28041
  data.translationOfBook,
27768
28042
  options
27769
28043
  ),
28044
+ userFeedbackTranslationUrl: this.toAstProperty(
28045
+ bitType,
28046
+ ConfigKey.property_userFeedbackTranslationUrl,
28047
+ data.userFeedbackTranslationUrl,
28048
+ options
28049
+ ),
28050
+ userFeedbackContentUrl: this.toAstProperty(
28051
+ bitType,
28052
+ ConfigKey.property_userFeedbackContentUrl,
28053
+ data.userFeedbackContentUrl,
28054
+ options
28055
+ ),
27770
28056
  author: this.toAstProperty(bitType, ConfigKey.property_author, data.author, options),
27771
28057
  subject: this.toAstProperty(bitType, ConfigKey.property_subject, data.subject, options),
27772
28058
  date: this.toAstProperty(bitType, ConfigKey.property_date, data.date, options),
@@ -31558,7 +31844,8 @@ var BitmarkGenerator = class extends AstWalkerGenerator {
31558
31844
  this.writeNL();
31559
31845
  }
31560
31846
  const content = cell.content ?? instance3.EMPTY_STRING;
31561
- this.writeTextOrValue(content, this.textFormat, TextLocation2.body);
31847
+ const options = this.options.noBreakscaping ? { noBreakscaping: true } : void 0;
31848
+ this.writeTextOrValue(content, this.textFormat, TextLocation2.body, options);
31562
31849
  }
31563
31850
  writeTableCellProperty(name, value) {
31564
31851
  this.writeOPA();
@@ -33024,6 +33311,278 @@ var BitmarkStringGenerator = class {
33024
33311
  }
33025
33312
  };
33026
33313
 
33314
+ // src/generator/html/HtmlTableGenerator.ts
33315
+ var MARK_TO_TAG = {
33316
+ bold: "b",
33317
+ italic: "i",
33318
+ superscript: "sup",
33319
+ subscript: "sub",
33320
+ strike: "s",
33321
+ del: "del",
33322
+ ins: "ins",
33323
+ underline: "u",
33324
+ highlight: "mark",
33325
+ code: "code"
33326
+ };
33327
+ var INLINE_IMAGE_RE = /==([^=]*(?:=(?!=)[^=]*)*)==\|imageInline:([^|\s]+)((?:\|(?:@?[A-Za-z][A-Za-z0-9_-]*:[^|\s]+|#[^|\s]*))*)\|?/g;
33328
+ var MAX_IMAGE_DIMENSION = 9999;
33329
+ var HtmlTableGenerator = class {
33330
+ constructor() {
33331
+ __publicField(this, "indentUnit", " ");
33332
+ }
33333
+ /**
33334
+ * Generate HTML table fragments from a string or JSON value.
33335
+ * @returns the concatenated `<table>` fragments, blank-line separated (empty string if none).
33336
+ */
33337
+ generate(input) {
33338
+ let data = input;
33339
+ if (typeof data === "string") {
33340
+ try {
33341
+ data = JSON.parse(data);
33342
+ } catch {
33343
+ return "";
33344
+ }
33345
+ }
33346
+ const bits = this.collectBits(data);
33347
+ const fragments = [];
33348
+ for (const bit of bits) {
33349
+ const html = this.renderTableBit(bit);
33350
+ if (html) fragments.push(html);
33351
+ }
33352
+ return fragments.join("\n\n");
33353
+ }
33354
+ // --- bit collection -------------------------------------------------------
33355
+ collectBits(data) {
33356
+ const out = [];
33357
+ const visit = (value) => {
33358
+ if (value == null || typeof value !== "object") return;
33359
+ if (Array.isArray(value)) {
33360
+ value.forEach(visit);
33361
+ return;
33362
+ }
33363
+ const obj = value;
33364
+ if ("bit" in obj && obj.bit && typeof obj.bit === "object") {
33365
+ out.push(obj.bit);
33366
+ } else if ("type" in obj) {
33367
+ out.push(obj);
33368
+ }
33369
+ };
33370
+ visit(data);
33371
+ return out;
33372
+ }
33373
+ // --- table rendering ------------------------------------------------------
33374
+ renderTableBit(bit) {
33375
+ const table = bit.table;
33376
+ if (!table) return "";
33377
+ if (bit.type !== "table" && bit.type !== "table-extended") return "";
33378
+ const extended = this.toExtended(table);
33379
+ const lines = [];
33380
+ lines.push("<table>");
33381
+ if (bit.caption != null) {
33382
+ const caption = this.cellContentToHtml(this.asTextAst(bit.caption));
33383
+ if (caption) lines.push(`${this.indentUnit}<caption>${caption}</caption>`);
33384
+ }
33385
+ this.renderSection(lines, "thead", extended.header, "header");
33386
+ this.renderSection(lines, "tbody", extended.body, "body");
33387
+ this.renderSection(lines, "tfoot", extended.footer, "footer");
33388
+ lines.push("</table>");
33389
+ return lines.join("\n");
33390
+ }
33391
+ toExtended(table) {
33392
+ const t = table;
33393
+ if (t.header || t.body || t.footer) return table;
33394
+ return convertBasicToExtendedTableFormat(table);
33395
+ }
33396
+ renderSection(lines, tag, section, key) {
33397
+ if (!section || !section.rows || section.rows.length === 0) return;
33398
+ lines.push(`${this.indentUnit}<${tag}>`);
33399
+ for (const row of section.rows) {
33400
+ lines.push(`${this.indentUnit.repeat(2)}<tr>`);
33401
+ for (const cell of row.cells ?? []) {
33402
+ lines.push(`${this.indentUnit.repeat(3)}${this.renderCell(cell, key)}`);
33403
+ }
33404
+ lines.push(`${this.indentUnit.repeat(2)}</tr>`);
33405
+ }
33406
+ lines.push(`${this.indentUnit}</${tag}>`);
33407
+ }
33408
+ renderCell(cell, _key) {
33409
+ const tag = cell.title === true ? "th" : "td";
33410
+ const attrs = [];
33411
+ if (cell.rowspan && cell.rowspan > 1) attrs.push(`rowspan="${cell.rowspan}"`);
33412
+ if (cell.colspan && cell.colspan > 1) attrs.push(`colspan="${cell.colspan}"`);
33413
+ if (cell.scope) attrs.push(`scope="${cell.scope}"`);
33414
+ if (cell.colwidth && cell.colwidth > 0) attrs.push(`width="${cell.colwidth}"`);
33415
+ const open = attrs.length > 0 ? `<${tag} ${attrs.join(" ")}>` : `<${tag}>`;
33416
+ return `${open}${this.cellContentToHtml(this.asTextAst(cell.content))}</${tag}>`;
33417
+ }
33418
+ // --- cell content (TextAst -> HTML) ---------------------------------------
33419
+ asTextAst(content) {
33420
+ if (content == null) return [];
33421
+ if (typeof content === "string") return [{ type: "text", text: content }];
33422
+ return content;
33423
+ }
33424
+ cellContentToHtml(blocks) {
33425
+ if (blocks.length === 0) return "";
33426
+ if (blocks.length === 1 && blocks[0].type === "paragraph") {
33427
+ return this.inlineToHtml(blocks[0].content ?? []);
33428
+ }
33429
+ return blocks.map((b) => this.blockToHtml(b)).join("");
33430
+ }
33431
+ blockToHtml(node) {
33432
+ switch (node.type) {
33433
+ case "paragraph":
33434
+ return `<p>${this.inlineToHtml(node.content ?? [])}</p>`;
33435
+ case "bulletList":
33436
+ case "noBulletList":
33437
+ case "orderedList":
33438
+ case "orderedListRoman":
33439
+ case "orderedListRomanLower":
33440
+ case "letteredList":
33441
+ case "letteredListLower":
33442
+ case "taskList":
33443
+ return this.listToHtml(node);
33444
+ case "image":
33445
+ case "imageInline":
33446
+ return this.imageToHtml(node);
33447
+ case "latex":
33448
+ return this.latexToHtml(node);
33449
+ default:
33450
+ return this.inlineToHtml([node]);
33451
+ }
33452
+ }
33453
+ inlineToHtml(nodes) {
33454
+ return nodes.map((n) => this.inlineNodeToHtml(n)).join("");
33455
+ }
33456
+ inlineNodeToHtml(node) {
33457
+ if (node.type === "hardBreak") return "<br>";
33458
+ if (node.type === "image" || node.type === "imageInline") return this.imageToHtml(node);
33459
+ if (node.type === "latex") return this.latexToHtml(node);
33460
+ if (node.type === "text") {
33461
+ return this.wrapMarks(this.textToHtml(node.text ?? ""), node.marks);
33462
+ }
33463
+ if (node.content) return this.inlineToHtml(node.content);
33464
+ return this.escapeText(node.text ?? "");
33465
+ }
33466
+ wrapMarks(text, marks) {
33467
+ if (!marks || marks.length === 0) return text;
33468
+ let open = "";
33469
+ let close = "";
33470
+ for (const mark of marks) {
33471
+ if (mark.type === "link") {
33472
+ const href = mark.attrs?.href ?? "";
33473
+ open += `<a href="${this.escapeAttr(href)}">`;
33474
+ close = `</a>${close}`;
33475
+ } else {
33476
+ const tag = MARK_TO_TAG[mark.type];
33477
+ if (tag) {
33478
+ open += `<${tag}>`;
33479
+ close = `</${tag}>${close}`;
33480
+ }
33481
+ }
33482
+ }
33483
+ return `${open}${text}${close}`;
33484
+ }
33485
+ // --- lists ----------------------------------------------------------------
33486
+ listToHtml(node) {
33487
+ const { tag, attrs } = this.listTag(node);
33488
+ const items = (node.content ?? []).map((item) => this.listItemToHtml(item, node.type === "taskList")).join("");
33489
+ return `<${tag}${attrs}>${items}</${tag}>`;
33490
+ }
33491
+ listTag(node) {
33492
+ const start = node.attrs?.start;
33493
+ const startAttr = typeof start === "number" && start !== 1 ? ` start="${start}"` : "";
33494
+ switch (node.type) {
33495
+ case "orderedList":
33496
+ return { tag: "ol", attrs: startAttr };
33497
+ case "orderedListRoman":
33498
+ return { tag: "ol", attrs: ` type="I"${startAttr}` };
33499
+ case "orderedListRomanLower":
33500
+ return { tag: "ol", attrs: ` type="i"${startAttr}` };
33501
+ case "letteredList":
33502
+ return { tag: "ol", attrs: ` type="A"${startAttr}` };
33503
+ case "letteredListLower":
33504
+ return { tag: "ol", attrs: ` type="a"${startAttr}` };
33505
+ case "noBulletList":
33506
+ return { tag: "ul", attrs: ' style="list-style:none"' };
33507
+ case "taskList":
33508
+ case "bulletList":
33509
+ default:
33510
+ return { tag: "ul", attrs: "" };
33511
+ }
33512
+ }
33513
+ listItemToHtml(item, task) {
33514
+ const inner = (item.content ?? []).map((b) => {
33515
+ if (b.type === "paragraph") return this.inlineToHtml(b.content ?? []);
33516
+ return this.blockToHtml(b);
33517
+ }).join("");
33518
+ if (task) {
33519
+ const checked = item.attrs?.checked ? " checked" : "";
33520
+ return `<li><input type="checkbox"${checked}>${inner}</li>`;
33521
+ }
33522
+ return `<li>${inner}</li>`;
33523
+ }
33524
+ // --- images ---------------------------------------------------------------
33525
+ imageToHtml(node) {
33526
+ const attrs = node.attrs ?? {};
33527
+ const parts = [];
33528
+ const src = attrs.src;
33529
+ parts.push(`src="${this.escapeAttr(typeof src === "string" ? src : "")}"`);
33530
+ if (typeof attrs.alt === "string") parts.push(`alt="${this.escapeAttr(attrs.alt)}"`);
33531
+ if (typeof attrs.title === "string") parts.push(`title="${this.escapeAttr(attrs.title)}"`);
33532
+ if (attrs.width != null) parts.push(`width="${attrs.width}"`);
33533
+ if (attrs.height != null) parts.push(`height="${attrs.height}"`);
33534
+ if (typeof attrs.class === "string" && attrs.class) {
33535
+ parts.push(`class="${this.escapeAttr(attrs.class)}"`);
33536
+ }
33537
+ return `<img ${parts.join(" ")}>`;
33538
+ }
33539
+ textToHtml(text) {
33540
+ let html = "";
33541
+ let lastIndex = 0;
33542
+ for (const match of text.matchAll(INLINE_IMAGE_RE)) {
33543
+ const index = match.index ?? 0;
33544
+ html += this.escapeText(text.slice(lastIndex, index));
33545
+ html += this.inlineImageTokenToHtml(match);
33546
+ lastIndex = index + match[0].length;
33547
+ }
33548
+ html += this.escapeText(text.slice(lastIndex));
33549
+ return html;
33550
+ }
33551
+ inlineImageTokenToHtml(match) {
33552
+ const attrs = {
33553
+ alt: match[1],
33554
+ src: match[2]
33555
+ };
33556
+ const rawAttrs = match[3] ?? "";
33557
+ for (const attr of rawAttrs.split("|")) {
33558
+ if (!attr || attr.startsWith("#")) continue;
33559
+ const colon = attr.indexOf(":");
33560
+ if (colon < 1) continue;
33561
+ const key = attr.slice(0, colon).replace(/^@/, "");
33562
+ const value = attr.slice(colon + 1);
33563
+ if (key === "width" || key === "height") {
33564
+ const n = Number.parseInt(value, 10);
33565
+ if (!Number.isNaN(n) && n > 0 && n <= MAX_IMAGE_DIMENSION) attrs[key] = n;
33566
+ } else if (key === "class" || key === "title") {
33567
+ attrs[key] = value;
33568
+ }
33569
+ }
33570
+ return this.imageToHtml({ type: "imageInline", attrs });
33571
+ }
33572
+ // --- formulas -------------------------------------------------------------
33573
+ latexToHtml(node) {
33574
+ const formula = node.attrs?.formula ?? "";
33575
+ return `<math alttext="${this.escapeAttr(formula)}"></math>`;
33576
+ }
33577
+ // --- escaping -------------------------------------------------------------
33578
+ escapeText(text) {
33579
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
33580
+ }
33581
+ escapeAttr(text) {
33582
+ return this.escapeText(text).replace(/"/g, "&quot;");
33583
+ }
33584
+ };
33585
+
33027
33586
  // src/generator/json/JsonGenerator.ts
33028
33587
  var import_superenum33 = require("@ncoderz/superenum");
33029
33588
  var MOVE_BODY_RECURSION_LIMIT = 5e3;
@@ -33326,6 +33885,7 @@ var JsonGenerator = class extends AstWalkerGenerator {
33326
33885
  this.addProperty(this.bitJson, k, values, { array: true });
33327
33886
  }
33328
33887
  }
33888
+ return false;
33329
33889
  }
33330
33890
  // bitmarkAst -> bits -> bitsValue -> cardNode -> cardBits -> cardBitsValue
33331
33891
  enter_cardBitsValue(node, route) {
@@ -37951,6 +38511,8 @@ function propertyContentProcessor(context, contentDepth, tagsConfig, content, ta
37951
38511
  jsonKey: void 0,
37952
38512
  exportJsonKey: void 0,
37953
38513
  hasExportJsonKey: false,
38514
+ htmlKey: void 0,
38515
+ hasHtmlKey: false,
37954
38516
  format: TagFormat.bitmarkText,
37955
38517
  values: void 0,
37956
38518
  defaultValue: void 0,
@@ -42330,6 +42892,539 @@ var BitmarkParser = class {
42330
42892
  }
42331
42893
  };
42332
42894
 
42895
+ // src/parser/html/HtmlDom.ts
42896
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
42897
+ "area",
42898
+ "base",
42899
+ "br",
42900
+ "col",
42901
+ "embed",
42902
+ "hr",
42903
+ "img",
42904
+ "input",
42905
+ "link",
42906
+ "meta",
42907
+ "param",
42908
+ "source",
42909
+ "track",
42910
+ "wbr"
42911
+ ]);
42912
+ var IMPLIED_CLOSE = {
42913
+ li: /* @__PURE__ */ new Set(["li"]),
42914
+ td: /* @__PURE__ */ new Set(["td", "th"]),
42915
+ th: /* @__PURE__ */ new Set(["td", "th"]),
42916
+ tr: /* @__PURE__ */ new Set(["td", "th", "tr"]),
42917
+ thead: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
42918
+ tbody: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
42919
+ tfoot: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
42920
+ p: /* @__PURE__ */ new Set(["p"]),
42921
+ option: /* @__PURE__ */ new Set(["option"])
42922
+ };
42923
+ var TAG_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9:-]*)((?:\s+(?:"[^"]*"|'[^']*'|[^<>"'])*)?)(\/)?>/g;
42924
+ var ATTR_RE = /([a-zA-Z_:][a-zA-Z0-9:._-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
42925
+ function parseAttrs(raw) {
42926
+ const attrs = {};
42927
+ if (!raw) return attrs;
42928
+ ATTR_RE.lastIndex = 0;
42929
+ let m;
42930
+ while ((m = ATTR_RE.exec(raw)) !== null) {
42931
+ const name = m[1].toLowerCase();
42932
+ let value = m[2] ?? "";
42933
+ if (value.length >= 2 && (value[0] === '"' || value[0] === "'")) {
42934
+ value = value.slice(1, -1);
42935
+ }
42936
+ attrs[name] = value;
42937
+ }
42938
+ return attrs;
42939
+ }
42940
+ function stripNonContent(html) {
42941
+ 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, "");
42942
+ }
42943
+ function tokenize(html) {
42944
+ const tokens = [];
42945
+ const clean = stripNonContent(html);
42946
+ TAG_RE.lastIndex = 0;
42947
+ let lastIndex = 0;
42948
+ let m;
42949
+ while ((m = TAG_RE.exec(clean)) !== null) {
42950
+ if (m.index > lastIndex) {
42951
+ tokens.push({ kind: "text", text: clean.slice(lastIndex, m.index) });
42952
+ }
42953
+ lastIndex = TAG_RE.lastIndex;
42954
+ const isClose = m[1] === "/";
42955
+ const name = m[2].toLowerCase();
42956
+ if (isClose) {
42957
+ tokens.push({ kind: "close", name });
42958
+ } else {
42959
+ tokens.push({
42960
+ kind: "open",
42961
+ name,
42962
+ attrs: parseAttrs(m[3]),
42963
+ selfClose: m[4] === "/"
42964
+ });
42965
+ }
42966
+ }
42967
+ if (lastIndex < clean.length) {
42968
+ tokens.push({ kind: "text", text: clean.slice(lastIndex) });
42969
+ }
42970
+ return tokens;
42971
+ }
42972
+ function parseHtml(html) {
42973
+ const root = { type: "element", name: "#root", attrs: {}, children: [] };
42974
+ const stack = [root];
42975
+ const top = () => stack[stack.length - 1];
42976
+ for (const token of tokenize(html)) {
42977
+ if (token.kind === "text") {
42978
+ if (token.text) top().children.push({ type: "text", value: token.text });
42979
+ continue;
42980
+ }
42981
+ if (token.kind === "open") {
42982
+ const name = token.name;
42983
+ const closes = IMPLIED_CLOSE[name];
42984
+ if (closes) {
42985
+ while (stack.length > 1 && closes.has(top().name)) stack.pop();
42986
+ }
42987
+ const el = {
42988
+ type: "element",
42989
+ name,
42990
+ attrs: token.attrs ?? {},
42991
+ children: []
42992
+ };
42993
+ top().children.push(el);
42994
+ if (!token.selfClose && !VOID_ELEMENTS.has(name)) {
42995
+ stack.push(el);
42996
+ }
42997
+ continue;
42998
+ }
42999
+ for (let i = stack.length - 1; i > 0; i--) {
43000
+ if (stack[i].name === token.name) {
43001
+ stack.length = i;
43002
+ break;
43003
+ }
43004
+ }
43005
+ }
43006
+ return root.children;
43007
+ }
43008
+ function childElements(el, names) {
43009
+ return el.children.filter((c) => c.type === "element" && names.has(c.name));
43010
+ }
43011
+
43012
+ // src/parser/html/HtmlTableParser.ts
43013
+ var MARK_TAGS = {
43014
+ b: "bold",
43015
+ strong: "bold",
43016
+ i: "italic",
43017
+ em: "italic",
43018
+ sup: "superscript",
43019
+ sub: "subscript",
43020
+ s: "strike",
43021
+ strike: "strike",
43022
+ del: "del",
43023
+ ins: "ins",
43024
+ u: "underline",
43025
+ mark: "highlight",
43026
+ code: "code"
43027
+ };
43028
+ var SECTION_TAGS = /* @__PURE__ */ new Set(["thead", "tbody", "tfoot"]);
43029
+ var ROW_TAGS = /* @__PURE__ */ new Set(["tr"]);
43030
+ var CELL_TAGS = /* @__PURE__ */ new Set(["td", "th"]);
43031
+ var LI_TAGS = /* @__PURE__ */ new Set(["li"]);
43032
+ var VALID_SCOPES = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
43033
+ var NAMED_ENTITIES = {
43034
+ amp: "&",
43035
+ lt: "<",
43036
+ gt: ">",
43037
+ quot: '"',
43038
+ apos: "'",
43039
+ nbsp: "\xA0"
43040
+ };
43041
+ function decodeEntities(text) {
43042
+ return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, (whole, body) => {
43043
+ if (body[0] === "#") {
43044
+ const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
43045
+ if (Number.isFinite(code) && code >= 0 && code <= 1114111) {
43046
+ try {
43047
+ return String.fromCodePoint(code);
43048
+ } catch {
43049
+ return whole;
43050
+ }
43051
+ }
43052
+ return whole;
43053
+ }
43054
+ return NAMED_ENTITIES[body] ?? whole;
43055
+ });
43056
+ }
43057
+ function intAttr(value) {
43058
+ if (value == null) return void 0;
43059
+ const n = Number.parseInt(value, 10);
43060
+ return Number.isInteger(n) ? n : void 0;
43061
+ }
43062
+ function isElement(node) {
43063
+ return node.type === "element";
43064
+ }
43065
+ var HtmlTableParser = class {
43066
+ constructor() {
43067
+ __publicField(this, "keepUnknownTags", false);
43068
+ }
43069
+ /**
43070
+ * Parse an HTML string and return one table bit per top-level `<table>`.
43071
+ */
43072
+ parse(html, options) {
43073
+ this.keepUnknownTags = options?.keepUnknownTags ?? false;
43074
+ const tableFormat = options?.tableFormat ?? "table-extended";
43075
+ const onWarning = options?.onWarning;
43076
+ const tables = this.collectTopLevelTables(parseHtml(html));
43077
+ const bits = [];
43078
+ for (const table of tables) {
43079
+ const bit = this.tableToBit(table, tableFormat, onWarning);
43080
+ if (bit) bits.push(bit);
43081
+ }
43082
+ return bits;
43083
+ }
43084
+ // --- table extraction -----------------------------------------------------
43085
+ /** Collect every `<table>` element, NOT descending into nested tables. */
43086
+ collectTopLevelTables(nodes) {
43087
+ const tables = [];
43088
+ const visit = (node) => {
43089
+ if (!isElement(node)) return;
43090
+ if (node.name === "table") {
43091
+ tables.push(node);
43092
+ return;
43093
+ }
43094
+ node.children.forEach(visit);
43095
+ };
43096
+ nodes.forEach(visit);
43097
+ return tables;
43098
+ }
43099
+ tableToBit(table, tableFormat, onWarning) {
43100
+ const header = [];
43101
+ const body = [];
43102
+ const footer = [];
43103
+ let caption;
43104
+ for (const child of table.children) {
43105
+ if (!isElement(child)) continue;
43106
+ if (child.name === "caption") {
43107
+ const capContent = this.inlineNodes(child.children, []);
43108
+ const trimmed = this.trimInline(capContent);
43109
+ if (trimmed.length > 0) caption = trimmed;
43110
+ } else if (SECTION_TAGS.has(child.name)) {
43111
+ const target = child.name === "thead" ? header : child.name === "tfoot" ? footer : body;
43112
+ for (const tr of childElements(child, ROW_TAGS)) {
43113
+ target.push(this.rowToCells(tr));
43114
+ }
43115
+ } else if (ROW_TAGS.has(child.name)) {
43116
+ body.push(this.rowToCells(child));
43117
+ }
43118
+ }
43119
+ if (header.length === 0 && body.length === 0 && footer.length === 0) {
43120
+ return void 0;
43121
+ }
43122
+ const tableExtended = {};
43123
+ if (header.length > 0) tableExtended.header = { rows: header };
43124
+ if (body.length > 0) tableExtended.body = { rows: body };
43125
+ if (footer.length > 0) tableExtended.footer = { rows: footer };
43126
+ let tableData = tableExtended;
43127
+ if (tableFormat === "table") {
43128
+ if (this.isLossyAsBasic(tableExtended)) {
43129
+ onWarning?.(
43130
+ "Converting an HTML table to [.table] loses rowspan/colspan/scope and multi-section structure; use --tableFormat table-extended to preserve them."
43131
+ );
43132
+ }
43133
+ tableData = convertExtendedToBasicTableFormat(tableExtended);
43134
+ }
43135
+ const bit = {
43136
+ type: tableFormat,
43137
+ format: "bitmark++",
43138
+ table: tableData
43139
+ };
43140
+ if (caption) bit.caption = caption;
43141
+ return bit;
43142
+ }
43143
+ rowToCells(tr) {
43144
+ const cells = [];
43145
+ for (const cell of childElements(tr, CELL_TAGS)) {
43146
+ cells.push(this.cellToJson(cell));
43147
+ }
43148
+ return { cells };
43149
+ }
43150
+ cellToJson(cell) {
43151
+ const json = {
43152
+ content: this.blockNodes(cell.children)
43153
+ };
43154
+ if (cell.name === "th") json.title = true;
43155
+ const rowspan = intAttr(cell.attrs.rowspan);
43156
+ if (rowspan != null && rowspan >= 2) json.rowspan = rowspan;
43157
+ const colspan = intAttr(cell.attrs.colspan);
43158
+ if (colspan != null && colspan >= 2) json.colspan = colspan;
43159
+ const scope = cell.attrs.scope?.toLowerCase();
43160
+ if (scope && VALID_SCOPES.has(scope)) {
43161
+ json.scope = scope;
43162
+ }
43163
+ const width = this.widthFromCell(cell);
43164
+ if (width != null) json.colwidth = width;
43165
+ return json;
43166
+ }
43167
+ widthFromCell(cell) {
43168
+ const attrWidth = intAttr(cell.attrs.width);
43169
+ if (attrWidth != null && attrWidth > 0) return attrWidth;
43170
+ const style = cell.attrs.style;
43171
+ if (style) {
43172
+ const m = style.match(/width\s*:\s*(\d+)\s*px/i);
43173
+ if (m) {
43174
+ const n = Number.parseInt(m[1], 10);
43175
+ if (Number.isInteger(n) && n > 0) return n;
43176
+ }
43177
+ }
43178
+ return void 0;
43179
+ }
43180
+ isLossyAsBasic(table) {
43181
+ if (table.footer) return true;
43182
+ if (table.header && table.header.rows.length > 1) return true;
43183
+ const sections = [table.header, table.body, table.footer];
43184
+ for (const section of sections) {
43185
+ if (!section) continue;
43186
+ for (const row of section.rows) {
43187
+ for (const cell of row.cells) {
43188
+ if (cell.rowspan || cell.colspan || cell.scope) return true;
43189
+ }
43190
+ }
43191
+ }
43192
+ return false;
43193
+ }
43194
+ // --- content conversion (HTML -> TextAst) ---------------------------------
43195
+ /**
43196
+ * Convert a cell/li's child nodes into block-level TextAst nodes (paragraphs, lists, images).
43197
+ */
43198
+ blockNodes(nodes) {
43199
+ const blocks = [];
43200
+ let inline = [];
43201
+ const flush = () => {
43202
+ const trimmed = this.trimInline(inline);
43203
+ inline = [];
43204
+ if (trimmed.length === 0) return;
43205
+ if (trimmed.length === 1 && trimmed[0].type === "imageInline") {
43206
+ blocks.push({ ...trimmed[0], type: "image" });
43207
+ } else {
43208
+ blocks.push({ type: "paragraph", content: trimmed, attrs: {} });
43209
+ }
43210
+ };
43211
+ for (const node of nodes) {
43212
+ if (!isElement(node)) {
43213
+ inline.push(...this.inlineText(node.value, []));
43214
+ continue;
43215
+ }
43216
+ const name = node.name;
43217
+ if (name === "p") {
43218
+ flush();
43219
+ const content = this.trimInline(this.inlineNodes(node.children, []));
43220
+ if (content.length > 0) {
43221
+ blocks.push({ type: "paragraph", content, attrs: {} });
43222
+ }
43223
+ } else if (this.isListTag(name)) {
43224
+ flush();
43225
+ const list = this.listNode(node);
43226
+ if (list) blocks.push(list);
43227
+ } else {
43228
+ inline.push(...this.inlineFromElement(node, []));
43229
+ }
43230
+ }
43231
+ flush();
43232
+ return blocks;
43233
+ }
43234
+ /** Convert child nodes to a flat list of inline TextNodes, applying the given marks. */
43235
+ inlineNodes(nodes, marks) {
43236
+ const out = [];
43237
+ for (const node of nodes) {
43238
+ if (!isElement(node)) {
43239
+ out.push(...this.inlineText(node.value, marks));
43240
+ } else {
43241
+ out.push(...this.inlineFromElement(node, marks));
43242
+ }
43243
+ }
43244
+ return out;
43245
+ }
43246
+ inlineFromElement(el, marks) {
43247
+ const name = el.name;
43248
+ if (name === "br") {
43249
+ return [{ type: "hardBreak" }];
43250
+ }
43251
+ if (name === "img") {
43252
+ return [this.imageNode(el, true)];
43253
+ }
43254
+ if (this.isMathTag(name)) {
43255
+ return [this.latexNode(el)];
43256
+ }
43257
+ const markType = MARK_TAGS[name];
43258
+ if (markType) {
43259
+ return this.inlineNodes(el.children, [...marks, { type: markType }]);
43260
+ }
43261
+ if (name === "a") {
43262
+ const linkMark = {
43263
+ type: "link",
43264
+ attrs: {
43265
+ href: el.attrs.href ?? "",
43266
+ ...el.attrs.target ? { target: el.attrs.target } : {}
43267
+ }
43268
+ };
43269
+ return this.inlineNodes(el.children, [...marks, linkMark]);
43270
+ }
43271
+ if (this.keepUnknownTags) {
43272
+ const open = this.serializeOpenTag(el);
43273
+ const close = `</${name}>`;
43274
+ return [
43275
+ ...this.inlineText(open, marks),
43276
+ ...this.inlineNodes(el.children, marks),
43277
+ ...this.inlineText(close, marks)
43278
+ ];
43279
+ }
43280
+ return this.inlineNodes(el.children, marks);
43281
+ }
43282
+ inlineText(raw, marks) {
43283
+ const text = decodeEntities(raw).replace(/\s+/g, " ");
43284
+ if (text === "") return [];
43285
+ const node = { text, type: "text" };
43286
+ if (marks.length > 0) node.marks = marks.map((m) => ({ ...m }));
43287
+ return [node];
43288
+ }
43289
+ serializeOpenTag(el) {
43290
+ const attrs = Object.entries(el.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${v}"`).join("");
43291
+ return `<${el.name}${attrs}>`;
43292
+ }
43293
+ // --- lists ----------------------------------------------------------------
43294
+ isListTag(name) {
43295
+ return name === "ul" || name === "ol";
43296
+ }
43297
+ listNode(el) {
43298
+ const items = childElements(el, LI_TAGS);
43299
+ if (items.length === 0) return void 0;
43300
+ const isTask = el.name === "ul" && items.some((li) => {
43301
+ const firstEl = li.children.find(isElement);
43302
+ return firstEl?.name === "input" && firstEl.attrs.type?.toLowerCase() === "checkbox";
43303
+ });
43304
+ if (isTask) {
43305
+ return {
43306
+ type: "taskList",
43307
+ content: items.map((li) => this.taskItemNode(li))
43308
+ };
43309
+ }
43310
+ const type = this.listType(el);
43311
+ const node = {
43312
+ type,
43313
+ content: items.map((li) => this.listItemNode(li))
43314
+ };
43315
+ if (el.name === "ol") {
43316
+ const start = intAttr(el.attrs.start);
43317
+ if (start != null) {
43318
+ node.attrs = { start };
43319
+ }
43320
+ }
43321
+ return node;
43322
+ }
43323
+ listType(el) {
43324
+ if (el.name === "ol") {
43325
+ switch (el.attrs.type) {
43326
+ case "i":
43327
+ return "orderedListRomanLower";
43328
+ case "I":
43329
+ return "orderedListRoman";
43330
+ case "a":
43331
+ return "letteredListLower";
43332
+ case "A":
43333
+ return "letteredList";
43334
+ default:
43335
+ return "orderedList";
43336
+ }
43337
+ }
43338
+ const style = el.attrs.style ?? "";
43339
+ if (/list-style(-type)?\s*:\s*none/i.test(style)) return "noBulletList";
43340
+ return "bulletList";
43341
+ }
43342
+ listItemNode(li) {
43343
+ let content = this.blockNodes(li.children);
43344
+ if (content.length === 0) {
43345
+ content = [{ type: "paragraph", content: [], attrs: {} }];
43346
+ }
43347
+ return { type: "listItem", content };
43348
+ }
43349
+ taskItemNode(li) {
43350
+ const checkbox = li.children.find(
43351
+ (c) => isElement(c) && c.name === "input" && c.attrs.type?.toLowerCase() === "checkbox"
43352
+ );
43353
+ const checked = checkbox ? "checked" in checkbox.attrs : false;
43354
+ const rest = li.children.filter((c) => c !== checkbox);
43355
+ let content = this.blockNodes(rest);
43356
+ if (content.length === 0) {
43357
+ content = [{ type: "paragraph", content: [], attrs: {} }];
43358
+ }
43359
+ return { type: "taskItem", attrs: { checked }, content };
43360
+ }
43361
+ // --- images ---------------------------------------------------------------
43362
+ imageNode(el, inline) {
43363
+ const attrs = { src: el.attrs.src ?? "" };
43364
+ attrs.alt = el.attrs.alt ?? null;
43365
+ attrs.title = el.attrs.title ?? null;
43366
+ const width = intAttr(el.attrs.width);
43367
+ if (width != null) attrs.width = width;
43368
+ const height = intAttr(el.attrs.height);
43369
+ if (height != null) attrs.height = height;
43370
+ if (el.attrs.class) attrs.class = el.attrs.class;
43371
+ return { type: inline ? "imageInline" : "image", attrs };
43372
+ }
43373
+ // --- formulas -------------------------------------------------------------
43374
+ isMathTag(name) {
43375
+ return this.localName(name) === "math";
43376
+ }
43377
+ latexNode(el) {
43378
+ return {
43379
+ type: "latex",
43380
+ attrs: {
43381
+ formula: this.latexFromMath(el)
43382
+ }
43383
+ };
43384
+ }
43385
+ latexFromMath(el) {
43386
+ const alt = el.attrs.alttext ?? el.attrs.alt ?? el.attrs.title;
43387
+ if (alt != null) return decodeEntities(alt);
43388
+ const annotation = this.findLatexAnnotation(el);
43389
+ if (annotation) return this.textContent(annotation).trim();
43390
+ return this.textContent(el).trim();
43391
+ }
43392
+ findLatexAnnotation(el) {
43393
+ for (const child of el.children) {
43394
+ if (!isElement(child)) continue;
43395
+ const lowerEncoding = (child.attrs.encoding ?? "").toLowerCase();
43396
+ if (this.localName(child.name) === "annotation" && (/\btex\b/.test(lowerEncoding) || /\blatex\b/.test(lowerEncoding))) {
43397
+ return child;
43398
+ }
43399
+ const nested = this.findLatexAnnotation(child);
43400
+ if (nested) return nested;
43401
+ }
43402
+ return void 0;
43403
+ }
43404
+ textContent(el) {
43405
+ return el.children.map((child) => isElement(child) ? this.textContent(child) : decodeEntities(child.value)).join("").replace(/\s+/g, " ");
43406
+ }
43407
+ localName(name) {
43408
+ return name.includes(":") ? name.slice(name.indexOf(":") + 1) : name;
43409
+ }
43410
+ // --- whitespace -----------------------------------------------------------
43411
+ /** Trim leading/trailing whitespace from inline content (edges only). */
43412
+ trimInline(nodes) {
43413
+ const out = nodes.slice();
43414
+ const isWs = (n) => n.type === "text" && /^\s*$/.test(n.text ?? "");
43415
+ while (out.length > 0 && isWs(out[0])) out.shift();
43416
+ if (out.length > 0 && out[0].type === "text") {
43417
+ out[0] = { ...out[0], text: (out[0].text ?? "").replace(/^\s+/, "") };
43418
+ }
43419
+ while (out.length > 0 && isWs(out[out.length - 1])) out.pop();
43420
+ if (out.length > 0 && out[out.length - 1].type === "text") {
43421
+ const last = out.length - 1;
43422
+ out[last] = { ...out[last], text: (out[last].text ?? "").replace(/\s+$/, "") };
43423
+ }
43424
+ return out.filter((n) => !(n.type === "text" && n.text === ""));
43425
+ }
43426
+ };
43427
+
42333
43428
  // src/BitmarkParserGenerator.ts
42334
43429
  var Input = {
42335
43430
  /**
@@ -42966,7 +44061,10 @@ var BitmarkParserGenerator = class {
42966
44061
  outputFile: opts.outputFile,
42967
44062
  fileOptions: opts.fileOptions,
42968
44063
  jsonOptions: opts.jsonOptions,
42969
- bitmarkOptions: opts.bitmarkOptions
44064
+ bitmarkOptions: {
44065
+ ...opts.bitmarkOptions,
44066
+ ...opts.noBreakscaping ? { noBreakscaping: true } : {}
44067
+ }
42970
44068
  });
42971
44069
  }
42972
44070
  const outputFormat = opts.outputFormat ?? "html";
@@ -43269,728 +44367,6 @@ var BoundingBoxIndex = class _BoundingBoxIndex {
43269
44367
  }
43270
44368
  };
43271
44369
 
43272
- // src/generator/html/HtmlTableGenerator.ts
43273
- var MARK_TO_TAG = {
43274
- bold: "b",
43275
- italic: "i",
43276
- superscript: "sup",
43277
- subscript: "sub",
43278
- strike: "s",
43279
- del: "del",
43280
- ins: "ins",
43281
- underline: "u",
43282
- highlight: "mark",
43283
- code: "code"
43284
- };
43285
- var HtmlTableGenerator2 = class {
43286
- constructor() {
43287
- __publicField(this, "indentUnit", " ");
43288
- }
43289
- /**
43290
- * Generate HTML table fragments from a string or JSON value.
43291
- * @returns the concatenated `<table>` fragments, blank-line separated (empty string if none).
43292
- */
43293
- generate(input) {
43294
- let data = input;
43295
- if (typeof data === "string") {
43296
- try {
43297
- data = JSON.parse(data);
43298
- } catch {
43299
- return "";
43300
- }
43301
- }
43302
- const bits = this.collectBits(data);
43303
- const fragments = [];
43304
- for (const bit of bits) {
43305
- const html = this.renderTableBit(bit);
43306
- if (html) fragments.push(html);
43307
- }
43308
- return fragments.join("\n\n");
43309
- }
43310
- // --- bit collection -------------------------------------------------------
43311
- collectBits(data) {
43312
- const out = [];
43313
- const visit = (value) => {
43314
- if (value == null || typeof value !== "object") return;
43315
- if (Array.isArray(value)) {
43316
- value.forEach(visit);
43317
- return;
43318
- }
43319
- const obj = value;
43320
- if ("bit" in obj && obj.bit && typeof obj.bit === "object") {
43321
- out.push(obj.bit);
43322
- } else if ("type" in obj) {
43323
- out.push(obj);
43324
- }
43325
- };
43326
- visit(data);
43327
- return out;
43328
- }
43329
- // --- table rendering ------------------------------------------------------
43330
- renderTableBit(bit) {
43331
- const table = bit.table;
43332
- if (!table) return "";
43333
- if (bit.type !== "table" && bit.type !== "table-extended") return "";
43334
- const extended = this.toExtended(table);
43335
- const lines = [];
43336
- lines.push("<table>");
43337
- if (bit.caption != null) {
43338
- const caption = this.cellContentToHtml(this.asTextAst(bit.caption));
43339
- if (caption) lines.push(`${this.indentUnit}<caption>${caption}</caption>`);
43340
- }
43341
- this.renderSection(lines, "thead", extended.header, "header");
43342
- this.renderSection(lines, "tbody", extended.body, "body");
43343
- this.renderSection(lines, "tfoot", extended.footer, "footer");
43344
- lines.push("</table>");
43345
- return lines.join("\n");
43346
- }
43347
- toExtended(table) {
43348
- const t = table;
43349
- if (t.header || t.body || t.footer) return table;
43350
- return convertBasicToExtendedTableFormat(table);
43351
- }
43352
- renderSection(lines, tag, section, key) {
43353
- if (!section || !section.rows || section.rows.length === 0) return;
43354
- lines.push(`${this.indentUnit}<${tag}>`);
43355
- for (const row of section.rows) {
43356
- lines.push(`${this.indentUnit.repeat(2)}<tr>`);
43357
- for (const cell of row.cells ?? []) {
43358
- lines.push(`${this.indentUnit.repeat(3)}${this.renderCell(cell, key)}`);
43359
- }
43360
- lines.push(`${this.indentUnit.repeat(2)}</tr>`);
43361
- }
43362
- lines.push(`${this.indentUnit}</${tag}>`);
43363
- }
43364
- renderCell(cell, _key) {
43365
- const tag = cell.title === true ? "th" : "td";
43366
- const attrs = [];
43367
- if (cell.rowspan && cell.rowspan > 1) attrs.push(`rowspan="${cell.rowspan}"`);
43368
- if (cell.colspan && cell.colspan > 1) attrs.push(`colspan="${cell.colspan}"`);
43369
- if (cell.scope) attrs.push(`scope="${cell.scope}"`);
43370
- if (cell.colwidth && cell.colwidth > 0) attrs.push(`width="${cell.colwidth}"`);
43371
- const open = attrs.length > 0 ? `<${tag} ${attrs.join(" ")}>` : `<${tag}>`;
43372
- return `${open}${this.cellContentToHtml(this.asTextAst(cell.content))}</${tag}>`;
43373
- }
43374
- // --- cell content (TextAst -> HTML) ---------------------------------------
43375
- asTextAst(content) {
43376
- if (content == null) return [];
43377
- if (typeof content === "string") return [{ type: "text", text: content }];
43378
- return content;
43379
- }
43380
- cellContentToHtml(blocks) {
43381
- if (blocks.length === 0) return "";
43382
- if (blocks.length === 1 && blocks[0].type === "paragraph") {
43383
- return this.inlineToHtml(blocks[0].content ?? []);
43384
- }
43385
- return blocks.map((b) => this.blockToHtml(b)).join("");
43386
- }
43387
- blockToHtml(node) {
43388
- switch (node.type) {
43389
- case "paragraph":
43390
- return `<p>${this.inlineToHtml(node.content ?? [])}</p>`;
43391
- case "bulletList":
43392
- case "noBulletList":
43393
- case "orderedList":
43394
- case "orderedListRoman":
43395
- case "orderedListRomanLower":
43396
- case "letteredList":
43397
- case "letteredListLower":
43398
- case "taskList":
43399
- return this.listToHtml(node);
43400
- case "image":
43401
- case "imageInline":
43402
- return this.imageToHtml(node);
43403
- default:
43404
- return this.inlineToHtml([node]);
43405
- }
43406
- }
43407
- inlineToHtml(nodes) {
43408
- return nodes.map((n) => this.inlineNodeToHtml(n)).join("");
43409
- }
43410
- inlineNodeToHtml(node) {
43411
- if (node.type === "hardBreak") return "<br>";
43412
- if (node.type === "image" || node.type === "imageInline") return this.imageToHtml(node);
43413
- if (node.type === "text") {
43414
- return this.wrapMarks(this.escapeText(node.text ?? ""), node.marks);
43415
- }
43416
- if (node.content) return this.inlineToHtml(node.content);
43417
- return this.escapeText(node.text ?? "");
43418
- }
43419
- wrapMarks(text, marks) {
43420
- if (!marks || marks.length === 0) return text;
43421
- let open = "";
43422
- let close = "";
43423
- for (const mark of marks) {
43424
- if (mark.type === "link") {
43425
- const href = mark.attrs?.href ?? "";
43426
- open += `<a href="${this.escapeAttr(href)}">`;
43427
- close = `</a>${close}`;
43428
- } else {
43429
- const tag = MARK_TO_TAG[mark.type];
43430
- if (tag) {
43431
- open += `<${tag}>`;
43432
- close = `</${tag}>${close}`;
43433
- }
43434
- }
43435
- }
43436
- return `${open}${text}${close}`;
43437
- }
43438
- // --- lists ----------------------------------------------------------------
43439
- listToHtml(node) {
43440
- const { tag, attrs } = this.listTag(node);
43441
- const items = (node.content ?? []).map((item) => this.listItemToHtml(item, node.type === "taskList")).join("");
43442
- return `<${tag}${attrs}>${items}</${tag}>`;
43443
- }
43444
- listTag(node) {
43445
- const start = node.attrs?.start;
43446
- const startAttr = typeof start === "number" && start !== 1 ? ` start="${start}"` : "";
43447
- switch (node.type) {
43448
- case "orderedList":
43449
- return { tag: "ol", attrs: startAttr };
43450
- case "orderedListRoman":
43451
- return { tag: "ol", attrs: ` type="I"${startAttr}` };
43452
- case "orderedListRomanLower":
43453
- return { tag: "ol", attrs: ` type="i"${startAttr}` };
43454
- case "letteredList":
43455
- return { tag: "ol", attrs: ` type="A"${startAttr}` };
43456
- case "letteredListLower":
43457
- return { tag: "ol", attrs: ` type="a"${startAttr}` };
43458
- case "noBulletList":
43459
- return { tag: "ul", attrs: ' style="list-style:none"' };
43460
- case "taskList":
43461
- case "bulletList":
43462
- default:
43463
- return { tag: "ul", attrs: "" };
43464
- }
43465
- }
43466
- listItemToHtml(item, task) {
43467
- const inner = (item.content ?? []).map((b) => {
43468
- if (b.type === "paragraph") return this.inlineToHtml(b.content ?? []);
43469
- return this.blockToHtml(b);
43470
- }).join("");
43471
- if (task) {
43472
- const checked = item.attrs?.checked ? " checked" : "";
43473
- return `<li><input type="checkbox"${checked}>${inner}</li>`;
43474
- }
43475
- return `<li>${inner}</li>`;
43476
- }
43477
- // --- images ---------------------------------------------------------------
43478
- imageToHtml(node) {
43479
- const attrs = node.attrs ?? {};
43480
- const parts = [];
43481
- const src = attrs.src;
43482
- parts.push(`src="${this.escapeAttr(typeof src === "string" ? src : "")}"`);
43483
- if (typeof attrs.alt === "string") parts.push(`alt="${this.escapeAttr(attrs.alt)}"`);
43484
- if (typeof attrs.title === "string") parts.push(`title="${this.escapeAttr(attrs.title)}"`);
43485
- if (attrs.width != null) parts.push(`width="${attrs.width}"`);
43486
- if (attrs.height != null) parts.push(`height="${attrs.height}"`);
43487
- if (typeof attrs.class === "string" && attrs.class) {
43488
- parts.push(`class="${this.escapeAttr(attrs.class)}"`);
43489
- }
43490
- return `<img ${parts.join(" ")}>`;
43491
- }
43492
- // --- escaping -------------------------------------------------------------
43493
- escapeText(text) {
43494
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
43495
- }
43496
- escapeAttr(text) {
43497
- return this.escapeText(text).replace(/"/g, "&quot;");
43498
- }
43499
- };
43500
-
43501
- // src/parser/html/HtmlDom.ts
43502
- var VOID_ELEMENTS = /* @__PURE__ */ new Set([
43503
- "area",
43504
- "base",
43505
- "br",
43506
- "col",
43507
- "embed",
43508
- "hr",
43509
- "img",
43510
- "input",
43511
- "link",
43512
- "meta",
43513
- "param",
43514
- "source",
43515
- "track",
43516
- "wbr"
43517
- ]);
43518
- var IMPLIED_CLOSE = {
43519
- li: /* @__PURE__ */ new Set(["li"]),
43520
- td: /* @__PURE__ */ new Set(["td", "th"]),
43521
- th: /* @__PURE__ */ new Set(["td", "th"]),
43522
- tr: /* @__PURE__ */ new Set(["td", "th", "tr"]),
43523
- thead: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
43524
- tbody: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
43525
- tfoot: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
43526
- p: /* @__PURE__ */ new Set(["p"]),
43527
- option: /* @__PURE__ */ new Set(["option"])
43528
- };
43529
- var TAG_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)((?:\s+(?:"[^"]*"|'[^']*'|[^<>"'])*)?)(\/)?>/g;
43530
- var ATTR_RE = /([a-zA-Z_:][a-zA-Z0-9:._-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
43531
- function parseAttrs(raw) {
43532
- const attrs = {};
43533
- if (!raw) return attrs;
43534
- ATTR_RE.lastIndex = 0;
43535
- let m;
43536
- while ((m = ATTR_RE.exec(raw)) !== null) {
43537
- const name = m[1].toLowerCase();
43538
- let value = m[2] ?? "";
43539
- if (value.length >= 2 && (value[0] === '"' || value[0] === "'")) {
43540
- value = value.slice(1, -1);
43541
- }
43542
- attrs[name] = value;
43543
- }
43544
- return attrs;
43545
- }
43546
- function stripNonContent(html) {
43547
- 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, "");
43548
- }
43549
- function tokenize(html) {
43550
- const tokens = [];
43551
- const clean = stripNonContent(html);
43552
- TAG_RE.lastIndex = 0;
43553
- let lastIndex = 0;
43554
- let m;
43555
- while ((m = TAG_RE.exec(clean)) !== null) {
43556
- if (m.index > lastIndex) {
43557
- tokens.push({ kind: "text", text: clean.slice(lastIndex, m.index) });
43558
- }
43559
- lastIndex = TAG_RE.lastIndex;
43560
- const isClose = m[1] === "/";
43561
- const name = m[2].toLowerCase();
43562
- if (isClose) {
43563
- tokens.push({ kind: "close", name });
43564
- } else {
43565
- tokens.push({
43566
- kind: "open",
43567
- name,
43568
- attrs: parseAttrs(m[3]),
43569
- selfClose: m[4] === "/"
43570
- });
43571
- }
43572
- }
43573
- if (lastIndex < clean.length) {
43574
- tokens.push({ kind: "text", text: clean.slice(lastIndex) });
43575
- }
43576
- return tokens;
43577
- }
43578
- function parseHtml(html) {
43579
- const root = { type: "element", name: "#root", attrs: {}, children: [] };
43580
- const stack = [root];
43581
- const top = () => stack[stack.length - 1];
43582
- for (const token of tokenize(html)) {
43583
- if (token.kind === "text") {
43584
- if (token.text) top().children.push({ type: "text", value: token.text });
43585
- continue;
43586
- }
43587
- if (token.kind === "open") {
43588
- const name = token.name;
43589
- const closes = IMPLIED_CLOSE[name];
43590
- if (closes) {
43591
- while (stack.length > 1 && closes.has(top().name)) stack.pop();
43592
- }
43593
- const el = {
43594
- type: "element",
43595
- name,
43596
- attrs: token.attrs ?? {},
43597
- children: []
43598
- };
43599
- top().children.push(el);
43600
- if (!token.selfClose && !VOID_ELEMENTS.has(name)) {
43601
- stack.push(el);
43602
- }
43603
- continue;
43604
- }
43605
- for (let i = stack.length - 1; i > 0; i--) {
43606
- if (stack[i].name === token.name) {
43607
- stack.length = i;
43608
- break;
43609
- }
43610
- }
43611
- }
43612
- return root.children;
43613
- }
43614
- function childElements(el, names) {
43615
- return el.children.filter((c) => c.type === "element" && names.has(c.name));
43616
- }
43617
-
43618
- // src/parser/html/HtmlTableParser.ts
43619
- var MARK_TAGS = {
43620
- b: "bold",
43621
- strong: "bold",
43622
- i: "italic",
43623
- em: "italic",
43624
- sup: "superscript",
43625
- sub: "subscript",
43626
- s: "strike",
43627
- strike: "strike",
43628
- del: "del",
43629
- ins: "ins",
43630
- u: "underline",
43631
- mark: "highlight",
43632
- code: "code"
43633
- };
43634
- var SECTION_TAGS = /* @__PURE__ */ new Set(["thead", "tbody", "tfoot"]);
43635
- var ROW_TAGS = /* @__PURE__ */ new Set(["tr"]);
43636
- var CELL_TAGS = /* @__PURE__ */ new Set(["td", "th"]);
43637
- var LI_TAGS = /* @__PURE__ */ new Set(["li"]);
43638
- var VALID_SCOPES = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
43639
- var NAMED_ENTITIES = {
43640
- amp: "&",
43641
- lt: "<",
43642
- gt: ">",
43643
- quot: '"',
43644
- apos: "'",
43645
- nbsp: "\xA0"
43646
- };
43647
- function decodeEntities(text) {
43648
- return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, (whole, body) => {
43649
- if (body[0] === "#") {
43650
- const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
43651
- if (Number.isFinite(code) && code >= 0 && code <= 1114111) {
43652
- try {
43653
- return String.fromCodePoint(code);
43654
- } catch {
43655
- return whole;
43656
- }
43657
- }
43658
- return whole;
43659
- }
43660
- return NAMED_ENTITIES[body] ?? whole;
43661
- });
43662
- }
43663
- function intAttr(value) {
43664
- if (value == null) return void 0;
43665
- const n = Number.parseInt(value, 10);
43666
- return Number.isInteger(n) ? n : void 0;
43667
- }
43668
- function isElement(node) {
43669
- return node.type === "element";
43670
- }
43671
- var HtmlTableParser2 = class {
43672
- constructor() {
43673
- __publicField(this, "keepUnknownTags", false);
43674
- }
43675
- /**
43676
- * Parse an HTML string and return one table bit per top-level `<table>`.
43677
- */
43678
- parse(html, options) {
43679
- this.keepUnknownTags = options?.keepUnknownTags ?? false;
43680
- const tableFormat = options?.tableFormat ?? "table-extended";
43681
- const onWarning = options?.onWarning;
43682
- const tables = this.collectTopLevelTables(parseHtml(html));
43683
- const bits = [];
43684
- for (const table of tables) {
43685
- const bit = this.tableToBit(table, tableFormat, onWarning);
43686
- if (bit) bits.push(bit);
43687
- }
43688
- return bits;
43689
- }
43690
- // --- table extraction -----------------------------------------------------
43691
- /** Collect every `<table>` element, NOT descending into nested tables. */
43692
- collectTopLevelTables(nodes) {
43693
- const tables = [];
43694
- const visit = (node) => {
43695
- if (!isElement(node)) return;
43696
- if (node.name === "table") {
43697
- tables.push(node);
43698
- return;
43699
- }
43700
- node.children.forEach(visit);
43701
- };
43702
- nodes.forEach(visit);
43703
- return tables;
43704
- }
43705
- tableToBit(table, tableFormat, onWarning) {
43706
- const header = [];
43707
- const body = [];
43708
- const footer = [];
43709
- let caption;
43710
- for (const child of table.children) {
43711
- if (!isElement(child)) continue;
43712
- if (child.name === "caption") {
43713
- const capContent = this.inlineNodes(child.children, []);
43714
- const trimmed = this.trimInline(capContent);
43715
- if (trimmed.length > 0) caption = trimmed;
43716
- } else if (SECTION_TAGS.has(child.name)) {
43717
- const target = child.name === "thead" ? header : child.name === "tfoot" ? footer : body;
43718
- for (const tr of childElements(child, ROW_TAGS)) {
43719
- target.push(this.rowToCells(tr));
43720
- }
43721
- } else if (ROW_TAGS.has(child.name)) {
43722
- body.push(this.rowToCells(child));
43723
- }
43724
- }
43725
- if (header.length === 0 && body.length === 0 && footer.length === 0) {
43726
- return void 0;
43727
- }
43728
- const tableExtended = {};
43729
- if (header.length > 0) tableExtended.header = { rows: header };
43730
- if (body.length > 0) tableExtended.body = { rows: body };
43731
- if (footer.length > 0) tableExtended.footer = { rows: footer };
43732
- let tableData = tableExtended;
43733
- if (tableFormat === "table") {
43734
- if (this.isLossyAsBasic(tableExtended)) {
43735
- onWarning?.(
43736
- "Converting an HTML table to [.table] loses rowspan/colspan/scope and multi-section structure; use --tableFormat table-extended to preserve them."
43737
- );
43738
- }
43739
- tableData = convertExtendedToBasicTableFormat(tableExtended);
43740
- }
43741
- const bit = {
43742
- type: tableFormat,
43743
- format: "bitmark++",
43744
- table: tableData
43745
- };
43746
- if (caption) bit.caption = caption;
43747
- return bit;
43748
- }
43749
- rowToCells(tr) {
43750
- const cells = [];
43751
- for (const cell of childElements(tr, CELL_TAGS)) {
43752
- cells.push(this.cellToJson(cell));
43753
- }
43754
- return { cells };
43755
- }
43756
- cellToJson(cell) {
43757
- const json = {
43758
- content: this.blockNodes(cell.children)
43759
- };
43760
- if (cell.name === "th") json.title = true;
43761
- const rowspan = intAttr(cell.attrs.rowspan);
43762
- if (rowspan != null && rowspan >= 2) json.rowspan = rowspan;
43763
- const colspan = intAttr(cell.attrs.colspan);
43764
- if (colspan != null && colspan >= 2) json.colspan = colspan;
43765
- const scope = cell.attrs.scope?.toLowerCase();
43766
- if (scope && VALID_SCOPES.has(scope)) {
43767
- json.scope = scope;
43768
- }
43769
- const width = this.widthFromCell(cell);
43770
- if (width != null) json.colwidth = width;
43771
- return json;
43772
- }
43773
- widthFromCell(cell) {
43774
- const attrWidth = intAttr(cell.attrs.width);
43775
- if (attrWidth != null && attrWidth > 0) return attrWidth;
43776
- const style = cell.attrs.style;
43777
- if (style) {
43778
- const m = style.match(/width\s*:\s*(\d+)\s*px/i);
43779
- if (m) {
43780
- const n = Number.parseInt(m[1], 10);
43781
- if (Number.isInteger(n) && n > 0) return n;
43782
- }
43783
- }
43784
- return void 0;
43785
- }
43786
- isLossyAsBasic(table) {
43787
- if (table.footer) return true;
43788
- if (table.header && table.header.rows.length > 1) return true;
43789
- const sections = [table.header, table.body, table.footer];
43790
- for (const section of sections) {
43791
- if (!section) continue;
43792
- for (const row of section.rows) {
43793
- for (const cell of row.cells) {
43794
- if (cell.rowspan || cell.colspan || cell.scope) return true;
43795
- }
43796
- }
43797
- }
43798
- return false;
43799
- }
43800
- // --- content conversion (HTML -> TextAst) ---------------------------------
43801
- /**
43802
- * Convert a cell/li's child nodes into block-level TextAst nodes (paragraphs, lists, images).
43803
- */
43804
- blockNodes(nodes) {
43805
- const blocks = [];
43806
- let inline = [];
43807
- const flush = () => {
43808
- const trimmed = this.trimInline(inline);
43809
- inline = [];
43810
- if (trimmed.length === 0) return;
43811
- if (trimmed.length === 1 && trimmed[0].type === "imageInline") {
43812
- blocks.push({ ...trimmed[0], type: "image" });
43813
- } else {
43814
- blocks.push({ type: "paragraph", content: trimmed, attrs: {} });
43815
- }
43816
- };
43817
- for (const node of nodes) {
43818
- if (!isElement(node)) {
43819
- inline.push(...this.inlineText(node.value, []));
43820
- continue;
43821
- }
43822
- const name = node.name;
43823
- if (name === "p") {
43824
- flush();
43825
- const content = this.trimInline(this.inlineNodes(node.children, []));
43826
- if (content.length > 0) {
43827
- blocks.push({ type: "paragraph", content, attrs: {} });
43828
- }
43829
- } else if (this.isListTag(name)) {
43830
- flush();
43831
- const list = this.listNode(node);
43832
- if (list) blocks.push(list);
43833
- } else {
43834
- inline.push(...this.inlineFromElement(node, []));
43835
- }
43836
- }
43837
- flush();
43838
- return blocks;
43839
- }
43840
- /** Convert child nodes to a flat list of inline TextNodes, applying the given marks. */
43841
- inlineNodes(nodes, marks) {
43842
- const out = [];
43843
- for (const node of nodes) {
43844
- if (!isElement(node)) {
43845
- out.push(...this.inlineText(node.value, marks));
43846
- } else {
43847
- out.push(...this.inlineFromElement(node, marks));
43848
- }
43849
- }
43850
- return out;
43851
- }
43852
- inlineFromElement(el, marks) {
43853
- const name = el.name;
43854
- if (name === "br") {
43855
- return [{ type: "hardBreak" }];
43856
- }
43857
- if (name === "img") {
43858
- return [this.imageNode(el, true)];
43859
- }
43860
- const markType = MARK_TAGS[name];
43861
- if (markType) {
43862
- return this.inlineNodes(el.children, [...marks, { type: markType }]);
43863
- }
43864
- if (name === "a") {
43865
- const linkMark = {
43866
- type: "link",
43867
- attrs: {
43868
- href: el.attrs.href ?? "",
43869
- ...el.attrs.target ? { target: el.attrs.target } : {}
43870
- }
43871
- };
43872
- return this.inlineNodes(el.children, [...marks, linkMark]);
43873
- }
43874
- if (this.keepUnknownTags) {
43875
- const open = this.serializeOpenTag(el);
43876
- const close = `</${name}>`;
43877
- return [
43878
- ...this.inlineText(open, marks),
43879
- ...this.inlineNodes(el.children, marks),
43880
- ...this.inlineText(close, marks)
43881
- ];
43882
- }
43883
- return this.inlineNodes(el.children, marks);
43884
- }
43885
- inlineText(raw, marks) {
43886
- const text = decodeEntities(raw).replace(/\s+/g, " ");
43887
- if (text === "") return [];
43888
- const node = { text, type: "text" };
43889
- if (marks.length > 0) node.marks = marks.map((m) => ({ ...m }));
43890
- return [node];
43891
- }
43892
- serializeOpenTag(el) {
43893
- const attrs = Object.entries(el.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${v}"`).join("");
43894
- return `<${el.name}${attrs}>`;
43895
- }
43896
- // --- lists ----------------------------------------------------------------
43897
- isListTag(name) {
43898
- return name === "ul" || name === "ol";
43899
- }
43900
- listNode(el) {
43901
- const items = childElements(el, LI_TAGS);
43902
- if (items.length === 0) return void 0;
43903
- const isTask = el.name === "ul" && items.some((li) => {
43904
- const firstEl = li.children.find(isElement);
43905
- return firstEl?.name === "input" && firstEl.attrs.type?.toLowerCase() === "checkbox";
43906
- });
43907
- if (isTask) {
43908
- return {
43909
- type: "taskList",
43910
- content: items.map((li) => this.taskItemNode(li))
43911
- };
43912
- }
43913
- const type = this.listType(el);
43914
- const node = {
43915
- type,
43916
- content: items.map((li) => this.listItemNode(li))
43917
- };
43918
- if (el.name === "ol") {
43919
- const start = intAttr(el.attrs.start);
43920
- if (start != null) {
43921
- node.attrs = { start };
43922
- }
43923
- }
43924
- return node;
43925
- }
43926
- listType(el) {
43927
- if (el.name === "ol") {
43928
- switch (el.attrs.type) {
43929
- case "i":
43930
- return "orderedListRomanLower";
43931
- case "I":
43932
- return "orderedListRoman";
43933
- case "a":
43934
- return "letteredListLower";
43935
- case "A":
43936
- return "letteredList";
43937
- default:
43938
- return "orderedList";
43939
- }
43940
- }
43941
- const style = el.attrs.style ?? "";
43942
- if (/list-style(-type)?\s*:\s*none/i.test(style)) return "noBulletList";
43943
- return "bulletList";
43944
- }
43945
- listItemNode(li) {
43946
- let content = this.blockNodes(li.children);
43947
- if (content.length === 0) {
43948
- content = [{ type: "paragraph", content: [], attrs: {} }];
43949
- }
43950
- return { type: "listItem", content };
43951
- }
43952
- taskItemNode(li) {
43953
- const checkbox = li.children.find(
43954
- (c) => isElement(c) && c.name === "input" && c.attrs.type?.toLowerCase() === "checkbox"
43955
- );
43956
- const checked = checkbox ? "checked" in checkbox.attrs : false;
43957
- const rest = li.children.filter((c) => c !== checkbox);
43958
- let content = this.blockNodes(rest);
43959
- if (content.length === 0) {
43960
- content = [{ type: "paragraph", content: [], attrs: {} }];
43961
- }
43962
- return { type: "taskItem", attrs: { checked }, content };
43963
- }
43964
- // --- images ---------------------------------------------------------------
43965
- imageNode(el, inline) {
43966
- const attrs = { src: el.attrs.src ?? "" };
43967
- attrs.alt = el.attrs.alt ?? null;
43968
- attrs.title = el.attrs.title ?? null;
43969
- const width = intAttr(el.attrs.width);
43970
- if (width != null) attrs.width = width;
43971
- const height = intAttr(el.attrs.height);
43972
- if (height != null) attrs.height = height;
43973
- if (el.attrs.class) attrs.class = el.attrs.class;
43974
- return { type: inline ? "imageInline" : "image", attrs };
43975
- }
43976
- // --- whitespace -----------------------------------------------------------
43977
- /** Trim leading/trailing whitespace from inline content (edges only). */
43978
- trimInline(nodes) {
43979
- const out = nodes.slice();
43980
- const isWs = (n) => n.type === "text" && /^\s*$/.test(n.text ?? "");
43981
- while (out.length > 0 && isWs(out[0])) out.shift();
43982
- if (out.length > 0 && out[0].type === "text") {
43983
- out[0] = { ...out[0], text: (out[0].text ?? "").replace(/^\s+/, "") };
43984
- }
43985
- while (out.length > 0 && isWs(out[out.length - 1])) out.pop();
43986
- if (out.length > 0 && out[out.length - 1].type === "text") {
43987
- const last = out.length - 1;
43988
- out[last] = { ...out[last], text: (out[last].text ?? "").replace(/\s+$/, "") };
43989
- }
43990
- return out.filter((n) => !(n.type === "text" && n.text === ""));
43991
- }
43992
- };
43993
-
43994
44370
  // src/index.ts
43995
44371
  init();
43996
44372
  // Annotate the CommonJS export names for ESM import in node: