@scrider/formatter 1.8.1 → 1.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -492,6 +492,13 @@ function isTextNode(node) {
492
492
  }
493
493
 
494
494
  // src/schema/blocks/table.ts
495
+ var VALID_CELL_HORIZONTAL_ALIGNS = [
496
+ "left",
497
+ "center",
498
+ "right",
499
+ "justify"
500
+ ];
501
+ var VALID_CELL_VERTICAL_ALIGNS = ["top", "middle", "bottom"];
495
502
  var CELL_KEY_RE = /^(\d+):(\d+)$/;
496
503
  function parseCellKey(key) {
497
504
  const match = CELL_KEY_RE.exec(key);
@@ -512,7 +519,22 @@ function getGridDimensions(cells) {
512
519
  return [maxRow + 1, maxCol + 1];
513
520
  }
514
521
  function isValidCellData(cell) {
515
- return typeof cell === "object" && cell !== null && Array.isArray(cell.ops) && cell.ops.length > 0 && (cell.colspan === void 0 || Number.isInteger(cell.colspan) && cell.colspan >= 1) && (cell.rowspan === void 0 || Number.isInteger(cell.rowspan) && cell.rowspan >= 1);
522
+ if (typeof cell !== "object" || cell === null || !Array.isArray(cell.ops) || cell.ops.length === 0) {
523
+ return false;
524
+ }
525
+ if (cell.colspan !== void 0 && (!Number.isInteger(cell.colspan) || cell.colspan < 1)) {
526
+ return false;
527
+ }
528
+ if (cell.rowspan !== void 0 && (!Number.isInteger(cell.rowspan) || cell.rowspan < 1)) {
529
+ return false;
530
+ }
531
+ if (cell.align !== void 0 && !VALID_CELL_HORIZONTAL_ALIGNS.includes(cell.align)) {
532
+ return false;
533
+ }
534
+ if (cell.vAlign !== void 0 && !VALID_CELL_VERTICAL_ALIGNS.includes(cell.vAlign)) {
535
+ return false;
536
+ }
537
+ return true;
516
538
  }
517
539
  function validateMergedCells(cells, rows, cols) {
518
540
  const covered = /* @__PURE__ */ new Set();
@@ -542,10 +564,44 @@ function validateMergedCells(cells, rows, cols) {
542
564
  }
543
565
  return true;
544
566
  }
567
+ function parseHorizontalAlign(value) {
568
+ const normalized = value?.trim().toLowerCase();
569
+ if (normalized === "left" || normalized === "center" || normalized === "right") {
570
+ return normalized;
571
+ }
572
+ if (normalized === "justify") return "justify";
573
+ return null;
574
+ }
575
+ function resolveCellHorizontalAlign(cell, col, colAligns) {
576
+ if (cell.align !== void 0) return cell.align;
577
+ const colAlign = colAligns?.[col];
578
+ if (colAlign) return colAlign;
579
+ return "left";
580
+ }
581
+ function promoteAlignFromCellOps(cell) {
582
+ if (cell.align !== void 0) return cell;
583
+ for (let i = cell.ops.length - 1; i >= 0; i--) {
584
+ const op = cell.ops[i];
585
+ if (!op || !(0, import_delta2.isInsert)(op) || !(0, import_delta2.isTextInsert)(op) || !op.insert.includes("\n")) continue;
586
+ const align = op.attributes?.align;
587
+ const parsed = typeof align === "string" ? parseHorizontalAlign(align) : null;
588
+ if (!parsed) continue;
589
+ const ops = cell.ops.map((item, idx) => {
590
+ if (idx !== i || !(0, import_delta2.isInsert)(item) || item.attributes == null) return item;
591
+ const rest = { ...item.attributes };
592
+ delete rest.align;
593
+ return Object.keys(rest).length > 0 ? { insert: item.insert, attributes: rest } : { insert: item.insert };
594
+ });
595
+ return { ...cell, ops, align: parsed };
596
+ }
597
+ return cell;
598
+ }
545
599
  function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
546
600
  const nl = pretty ? "\n" : "";
547
601
  const ind = (level) => pretty ? " ".repeat(level) : "";
548
- let html = `${ind(2)}<tr>${nl}`;
602
+ const rowHeight = data.rowHeights?.[row];
603
+ const trStyle = rowHeight !== void 0 && rowHeight > 0 ? ` style="height: ${rowHeight}px"` : "";
604
+ let html = `${ind(2)}<tr${trStyle}>${nl}`;
549
605
  for (let c = 0; c < cols; c++) {
550
606
  const key = `${row}:${c}`;
551
607
  const cell = data.cells[key];
@@ -559,11 +615,16 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
559
615
  if (cell.rowspan && cell.rowspan > 1) {
560
616
  attrs.push(`rowspan="${cell.rowspan}"`);
561
617
  }
562
- if (data.colAligns) {
563
- const align = data.colAligns[c];
564
- if (align && align !== "left") {
565
- attrs.push(`style="text-align: ${align}"`);
566
- }
618
+ const colDefault = data.colAligns?.[c] ?? "left";
619
+ const effectiveAlign = resolveCellHorizontalAlign(cell, c, data.colAligns);
620
+ let alignStyle = null;
621
+ if (effectiveAlign !== "left") {
622
+ alignStyle = `text-align: ${effectiveAlign}`;
623
+ } else if (cell.align === "left" && colDefault !== "left") {
624
+ alignStyle = "text-align: left";
625
+ }
626
+ if (alignStyle) {
627
+ attrs.push(`style="${alignStyle}"`);
567
628
  }
568
629
  const attrStr = attrs.length > 0 ? " " + attrs.join(" ") : "";
569
630
  let content = "";
@@ -579,6 +640,12 @@ function isGfmCompatible(data) {
579
640
  if (data.colWidths && data.colWidths.some((w) => w > 0)) {
580
641
  return false;
581
642
  }
643
+ if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
644
+ return false;
645
+ }
646
+ for (const cell of Object.values(data.cells)) {
647
+ if (cell !== null && cell.align !== void 0) return false;
648
+ }
582
649
  for (const cell of Object.values(data.cells)) {
583
650
  if (cell === null) return false;
584
651
  if (cell.colspan && cell.colspan > 1) return false;
@@ -688,15 +755,62 @@ function extractColWidths(table) {
688
755
  }
689
756
  return void 0;
690
757
  }
758
+ function extractRowHeightPx(row) {
759
+ const style = row.getAttribute("style") || "";
760
+ const heightMatch = style.match(/(?:^|;)\s*height:\s*([\d.]+)px/i);
761
+ if (heightMatch?.[1]) {
762
+ const n = parseFloat(heightMatch[1]);
763
+ if (Number.isFinite(n) && n > 0) return n;
764
+ }
765
+ const minHeightMatch = style.match(/(?:^|;)\s*min-height:\s*([\d.]+)px/i);
766
+ if (minHeightMatch?.[1]) {
767
+ const n = parseFloat(minHeightMatch[1]);
768
+ if (Number.isFinite(n) && n > 0) return n;
769
+ }
770
+ const heightAttr = row.getAttribute("height");
771
+ if (heightAttr) {
772
+ const n = parseFloat(heightAttr);
773
+ if (Number.isFinite(n) && n > 0) return n;
774
+ }
775
+ return null;
776
+ }
777
+ function extractRowHeights(rows) {
778
+ const heights = [];
779
+ let any = false;
780
+ for (const row of rows) {
781
+ const h = extractRowHeightPx(row);
782
+ if (h != null) {
783
+ heights.push(h);
784
+ any = true;
785
+ } else {
786
+ heights.push(0);
787
+ }
788
+ }
789
+ return any ? heights : void 0;
790
+ }
691
791
  function extractCellAlign(cell) {
692
- const textAlign = cell.style?.textAlign || cell.style?.getPropertyValue?.("text-align");
693
- if (textAlign === "left" || textAlign === "center" || textAlign === "right") {
694
- return textAlign;
792
+ const direct = extractInlineHorizontalAlign(cell);
793
+ if (direct) return direct;
794
+ const children = cell.childNodes;
795
+ for (let i = 0; i < children.length; i++) {
796
+ const child = children[i];
797
+ if (!child || !isElement(child)) continue;
798
+ const tag = child.tagName.toLowerCase();
799
+ if (tag === "p" || tag === "div") {
800
+ const align = extractInlineHorizontalAlign(child);
801
+ if (align) return align;
802
+ }
695
803
  }
696
- const style = cell.getAttribute("style") || "";
697
- const match = style.match(/text-align:\s*(left|center|right)/);
804
+ return null;
805
+ }
806
+ function extractInlineHorizontalAlign(element) {
807
+ const textAlign = element.style?.textAlign || element.style?.getPropertyValue?.("text-align");
808
+ const fromStyle = parseHorizontalAlign(textAlign);
809
+ if (fromStyle) return fromStyle;
810
+ const style = element.getAttribute("style") || "";
811
+ const match = style.match(/text-align:\s*(left|center|right|justify)/i);
698
812
  if (match?.[1]) {
699
- return match[1];
813
+ return parseHorizontalAlign(match[1]);
700
814
  }
701
815
  return null;
702
816
  }
@@ -705,6 +819,7 @@ function parseTableElement(table, context) {
705
819
  if (rows.length === 0) return null;
706
820
  const cells = {};
707
821
  const colAligns = [];
822
+ const rawAligns = {};
708
823
  let maxCol = 0;
709
824
  let firstRowProcessed = false;
710
825
  const occupied = /* @__PURE__ */ new Set();
@@ -733,10 +848,20 @@ function parseTableElement(table, context) {
733
848
  } else {
734
849
  ops = [{ insert: "\n" }];
735
850
  }
736
- const cellData = { ops };
851
+ let cellData = { ops };
737
852
  if (colspan > 1) cellData.colspan = colspan;
738
853
  if (rowspan > 1) cellData.rowspan = rowspan;
739
- cells[`${rowIdx}:${colIdx}`] = cellData;
854
+ cellData = promoteAlignFromCellOps(cellData);
855
+ const htmlAlign = extractCellAlign(cell);
856
+ const effectiveAlign = htmlAlign ?? cellData.align ?? "left";
857
+ if (cellData.align !== void 0) {
858
+ const withoutAlign = { ...cellData };
859
+ delete withoutAlign.align;
860
+ cellData = withoutAlign;
861
+ }
862
+ const cellKey = `${rowIdx}:${colIdx}`;
863
+ cells[cellKey] = cellData;
864
+ rawAligns[cellKey] = effectiveAlign;
740
865
  for (let dr = 0; dr < rowspan; dr++) {
741
866
  for (let dc = 0; dc < colspan; dc++) {
742
867
  if (dr === 0 && dc === 0) continue;
@@ -750,8 +875,9 @@ function parseTableElement(table, context) {
750
875
  }
751
876
  if (!firstRowProcessed) {
752
877
  const align = extractCellAlign(cell);
878
+ const colAlign = align === "center" || align === "right" ? align : null;
753
879
  for (let dc = 0; dc < colspan; dc++) {
754
- colAligns.push(align);
880
+ colAligns.push(colAlign);
755
881
  }
756
882
  }
757
883
  const cellEndCol = colIdx + colspan - 1;
@@ -787,11 +913,31 @@ function parseTableElement(table, context) {
787
913
  if (colWidths.length > totalCols) colWidths.length = totalCols;
788
914
  result.colWidths = colWidths;
789
915
  }
916
+ const rowHeights = extractRowHeights(rows);
917
+ if (rowHeights) {
918
+ while (rowHeights.length < totalRows) rowHeights.push(0);
919
+ if (rowHeights.length > totalRows) rowHeights.length = totalRows;
920
+ if (rowHeights.some((h) => h > 0)) {
921
+ result.rowHeights = rowHeights;
922
+ }
923
+ }
790
924
  if (colAligns.length > 0 && colAligns.some((a) => a !== null)) {
791
925
  while (colAligns.length < totalCols) colAligns.push(null);
792
926
  if (colAligns.length > totalCols) colAligns.length = totalCols;
793
927
  result.colAligns = colAligns;
794
928
  }
929
+ for (let r = 0; r < totalRows; r++) {
930
+ for (let c = 0; c < totalCols; c++) {
931
+ const key = `${r}:${c}`;
932
+ const cell = result.cells[key];
933
+ if (cell == null) continue;
934
+ const colDefault = result.colAligns?.[c] ?? "left";
935
+ const effective = rawAligns[key] ?? "left";
936
+ if (effective !== colDefault) {
937
+ result.cells[key] = { ...cell, align: effective };
938
+ }
939
+ }
940
+ }
795
941
  return result;
796
942
  }
797
943
  var tableBlockHandler = {
@@ -834,6 +980,14 @@ var tableBlockHandler = {
834
980
  if (typeof w !== "number" || w < 0) return false;
835
981
  }
836
982
  }
983
+ if (data.rowHeights !== void 0) {
984
+ if (!Array.isArray(data.rowHeights) || data.rowHeights.length !== rows) {
985
+ return false;
986
+ }
987
+ for (const h of data.rowHeights) {
988
+ if (typeof h !== "number" || h < 0) return false;
989
+ }
990
+ }
837
991
  if (data.colAligns !== void 0) {
838
992
  if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
839
993
  return false;
@@ -903,8 +1057,11 @@ var tableBlockHandler = {
903
1057
  for (const [key, cell] of Object.entries(data.cells)) {
904
1058
  if (cell !== null) {
905
1059
  const normalized = normalizeDelta(new import_delta2.Delta(cell.ops), registry);
906
- if (normalized.ops !== cell.ops) {
907
- newCells[key] = { ...cell, ops: normalized.ops };
1060
+ const promoted = promoteAlignFromCellOps(
1061
+ normalized.ops !== cell.ops ? { ...cell, ops: normalized.ops } : cell
1062
+ );
1063
+ if (promoted.ops !== cell.ops || promoted.align !== cell.align) {
1064
+ newCells[key] = promoted;
908
1065
  changed = true;
909
1066
  } else {
910
1067
  newCells[key] = cell;