@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.d.cts CHANGED
@@ -481,9 +481,13 @@ declare class BlockHandlerRegistry {
481
481
  }
482
482
 
483
483
  /**
484
- * Cell alignment options
484
+ * Column alignment options (GFM column-level subset).
485
485
  */
486
486
  type CellAlign = 'left' | 'center' | 'right';
487
+ /** Per-cell horizontal alignment (includes justify; not used in colAligns). */
488
+ type CellHorizontalAlign = CellAlign | 'justify';
489
+ /** Per-cell vertical alignment (schema in 1.8.2; HTML round-trip in 1.8.4). */
490
+ type CellVerticalAlign = 'top' | 'middle' | 'bottom';
487
491
  /**
488
492
  * Data for a single cell in an Extended Table.
489
493
  *
@@ -497,6 +501,10 @@ interface CellData {
497
501
  colspan?: number;
498
502
  /** Number of rows this cell spans (default: 1) */
499
503
  rowspan?: number;
504
+ /** Per-cell horizontal override (default: inherit column via colAligns). */
505
+ align?: CellHorizontalAlign;
506
+ /** Per-cell vertical alignment (HTML ingest/emit in formatter 1.8.4). */
507
+ vAlign?: CellVerticalAlign;
500
508
  }
501
509
  /**
502
510
  * Extended Table block data.
@@ -515,6 +523,8 @@ interface TableBlockData {
515
523
  headerRows?: number;
516
524
  /** Column widths (percentages by default) */
517
525
  colWidths?: number[];
526
+ /** Row heights in pixels (optional; auto when absent). */
527
+ rowHeights?: number[];
518
528
  /** Column alignments */
519
529
  colAligns?: (CellAlign | null)[];
520
530
  /** Cells indexed by "row:col" */
@@ -2052,4 +2062,4 @@ declare function collectAdjacentTableLines<T extends {
2052
2062
  */
2053
2063
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
2054
2064
 
2055
- export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isAdjacentSimpleTableGridBoundary, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
2065
+ export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type CellHorizontalAlign, type CellVerticalAlign, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isAdjacentSimpleTableGridBoundary, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.d.ts CHANGED
@@ -481,9 +481,13 @@ declare class BlockHandlerRegistry {
481
481
  }
482
482
 
483
483
  /**
484
- * Cell alignment options
484
+ * Column alignment options (GFM column-level subset).
485
485
  */
486
486
  type CellAlign = 'left' | 'center' | 'right';
487
+ /** Per-cell horizontal alignment (includes justify; not used in colAligns). */
488
+ type CellHorizontalAlign = CellAlign | 'justify';
489
+ /** Per-cell vertical alignment (schema in 1.8.2; HTML round-trip in 1.8.4). */
490
+ type CellVerticalAlign = 'top' | 'middle' | 'bottom';
487
491
  /**
488
492
  * Data for a single cell in an Extended Table.
489
493
  *
@@ -497,6 +501,10 @@ interface CellData {
497
501
  colspan?: number;
498
502
  /** Number of rows this cell spans (default: 1) */
499
503
  rowspan?: number;
504
+ /** Per-cell horizontal override (default: inherit column via colAligns). */
505
+ align?: CellHorizontalAlign;
506
+ /** Per-cell vertical alignment (HTML ingest/emit in formatter 1.8.4). */
507
+ vAlign?: CellVerticalAlign;
500
508
  }
501
509
  /**
502
510
  * Extended Table block data.
@@ -515,6 +523,8 @@ interface TableBlockData {
515
523
  headerRows?: number;
516
524
  /** Column widths (percentages by default) */
517
525
  colWidths?: number[];
526
+ /** Row heights in pixels (optional; auto when absent). */
527
+ rowHeights?: number[];
518
528
  /** Column alignments */
519
529
  colAligns?: (CellAlign | null)[];
520
530
  /** Cells indexed by "row:col" */
@@ -2052,4 +2062,4 @@ declare function collectAdjacentTableLines<T extends {
2052
2062
  */
2053
2063
  declare function extractTableRegion(ops: readonly Op[], hintOpIdx: number): TableRegion | null;
2054
2064
 
2055
- export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isAdjacentSimpleTableGridBoundary, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
2065
+ export { ALERT_TYPES, type AlertBlockData, type AlertType, type AlignType, BOX_FLOAT_VALUES, BOX_OVERFLOW_VALUES, type BlockContext, type BlockHandler, BlockHandlerRegistry, type BlockRenderOptions, type BoxBlockData, type BoxFloat, type BoxOpAttributes, type BoxOverflow, BrowserDOMAdapter, type CellAlign, type CellData, type CellHorizontalAlign, type CellVerticalAlign, type ColumnsBlockData, type DOMAdapter, type DOMDocument, type DOMDocumentFragment, type DOMElement, type DOMNode, type DOMNodeList, type DeltaToHtmlOptions, type DeltaToMarkdownOptions, type DocumentPresentation, type EmbedIsolationOptions, type FootnotesBlockData, type Format, type FormatDefinition, type FormatMatchResult, type FormatRenderContext, type FormatScope, type HtmlToDeltaOptions, LINE_HEIGHT_BLOCK_TAGS, type ListType, type MarkdownToDeltaOptions, NODE_TYPE, NodeDOMAdapter, PARAGRAPH_SPACING_BLOCK_TAGS, Registry, type ResolvedDocumentPresentation, type ResolvedTablePresentation, SCRIDER_LINE_HEIGHT_KEY, SCRIDER_MARGIN_AFTER_KEY, SCRIDER_MARGIN_BEFORE_KEY, type SanitizeOptions, type TableBlockData, type TableCellAlign, type TableCellCoords, type TableColAlignType, type TablePresentation, type TableRegion, alertBlockHandler, alignFormat, backgroundFormat, blockFormat, blockLineHeightStyleParts, blockMarginAfterStyleParts, blockMarginBeforeStyleParts, blockParagraphMarginStyleParts, blockPresentationStyleParts, blockquoteFormat, boldFormat, boxBlockHandler, browserAdapter, cloneDelta, codeBlockFormat, codeFormat, codeWidgetFormat, collectAdjacentTableLines, colorFormat, columnsBlockHandler, createDefaultBlockHandlers, createDefaultRegistry, defaultBlockFormats, defaultEmbedFormats, defaultFormats, defaultInlineFormats, deltaToHtml, deltaToMarkdown, dividerFormat, documentPresentationStyleParts, escapeHtml, extractBoxOpAttributes, extractTableRegion, fontFormat, footnoteRefFormat, footnotesBlockHandler, formulaFormat, getAdapter, getNamedColors, headerFormat, headerIdFormat, htmlToDelta, imageFormat, indentFormat, isAdapterAvailable, isAdjacentSimpleTableGridBoundary, isElement, isRemarkAvailable, isTableNewlineOp, isTextNode, isValidColor, isValidHexColor, isZebraBodyRow, italicFormat, kbdFormat, linkFormat, listFormat, markFormat, markdownToDelta, markdownToDeltaSync, nodeAdapter, normalizeDelta, parseScriderLineHeightMultiplier, parseScriderMarginAfterEm, parseScriderMarginBeforeEm, parseScriderMarginEm, preloadRemark, resolveDocumentPresentation, resolveTablePresentation, sanitizeDelta, sizeFormat, slugify, slugifyWithDedup, softBreakFormat, strikeFormat, subscriptFormat, superscriptFormat, tableBlockHandler, tableCellCoordsFromAttributes, tableCellCoordsFromOp, tableColAlignFormat, tableColFormat, tableHeaderFormat, tableRowFormat, toCodeWidgetEmbedUrl, toHexColor, underlineFormat, unescapeHtml, validateDelta, videoFormat };
package/dist/index.js CHANGED
@@ -199,7 +199,7 @@ var BlockHandlerRegistry = class {
199
199
  };
200
200
 
201
201
  // src/schema/blocks/table.ts
202
- import { Delta as Delta2 } from "@scrider/delta";
202
+ import { Delta as Delta2, isInsert as isInsert2, isTextInsert } from "@scrider/delta";
203
203
 
204
204
  // src/conversion/sanitize.ts
205
205
  import { Delta, isInsert, isRetain, isEmbedInsert, deepClone } from "@scrider/delta";
@@ -362,6 +362,13 @@ function isTextNode(node) {
362
362
  }
363
363
 
364
364
  // src/schema/blocks/table.ts
365
+ var VALID_CELL_HORIZONTAL_ALIGNS = [
366
+ "left",
367
+ "center",
368
+ "right",
369
+ "justify"
370
+ ];
371
+ var VALID_CELL_VERTICAL_ALIGNS = ["top", "middle", "bottom"];
365
372
  var CELL_KEY_RE = /^(\d+):(\d+)$/;
366
373
  function parseCellKey(key) {
367
374
  const match = CELL_KEY_RE.exec(key);
@@ -382,7 +389,22 @@ function getGridDimensions(cells) {
382
389
  return [maxRow + 1, maxCol + 1];
383
390
  }
384
391
  function isValidCellData(cell) {
385
- 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);
392
+ if (typeof cell !== "object" || cell === null || !Array.isArray(cell.ops) || cell.ops.length === 0) {
393
+ return false;
394
+ }
395
+ if (cell.colspan !== void 0 && (!Number.isInteger(cell.colspan) || cell.colspan < 1)) {
396
+ return false;
397
+ }
398
+ if (cell.rowspan !== void 0 && (!Number.isInteger(cell.rowspan) || cell.rowspan < 1)) {
399
+ return false;
400
+ }
401
+ if (cell.align !== void 0 && !VALID_CELL_HORIZONTAL_ALIGNS.includes(cell.align)) {
402
+ return false;
403
+ }
404
+ if (cell.vAlign !== void 0 && !VALID_CELL_VERTICAL_ALIGNS.includes(cell.vAlign)) {
405
+ return false;
406
+ }
407
+ return true;
386
408
  }
387
409
  function validateMergedCells(cells, rows, cols) {
388
410
  const covered = /* @__PURE__ */ new Set();
@@ -412,10 +434,44 @@ function validateMergedCells(cells, rows, cols) {
412
434
  }
413
435
  return true;
414
436
  }
437
+ function parseHorizontalAlign(value) {
438
+ const normalized = value?.trim().toLowerCase();
439
+ if (normalized === "left" || normalized === "center" || normalized === "right") {
440
+ return normalized;
441
+ }
442
+ if (normalized === "justify") return "justify";
443
+ return null;
444
+ }
445
+ function resolveCellHorizontalAlign(cell, col, colAligns) {
446
+ if (cell.align !== void 0) return cell.align;
447
+ const colAlign = colAligns?.[col];
448
+ if (colAlign) return colAlign;
449
+ return "left";
450
+ }
451
+ function promoteAlignFromCellOps(cell) {
452
+ if (cell.align !== void 0) return cell;
453
+ for (let i = cell.ops.length - 1; i >= 0; i--) {
454
+ const op = cell.ops[i];
455
+ if (!op || !isInsert2(op) || !isTextInsert(op) || !op.insert.includes("\n")) continue;
456
+ const align = op.attributes?.align;
457
+ const parsed = typeof align === "string" ? parseHorizontalAlign(align) : null;
458
+ if (!parsed) continue;
459
+ const ops = cell.ops.map((item, idx) => {
460
+ if (idx !== i || !isInsert2(item) || item.attributes == null) return item;
461
+ const rest = { ...item.attributes };
462
+ delete rest.align;
463
+ return Object.keys(rest).length > 0 ? { insert: item.insert, attributes: rest } : { insert: item.insert };
464
+ });
465
+ return { ...cell, ops, align: parsed };
466
+ }
467
+ return cell;
468
+ }
415
469
  function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
416
470
  const nl = pretty ? "\n" : "";
417
471
  const ind = (level) => pretty ? " ".repeat(level) : "";
418
- let html = `${ind(2)}<tr>${nl}`;
472
+ const rowHeight = data.rowHeights?.[row];
473
+ const trStyle = rowHeight !== void 0 && rowHeight > 0 ? ` style="height: ${rowHeight}px"` : "";
474
+ let html = `${ind(2)}<tr${trStyle}>${nl}`;
419
475
  for (let c = 0; c < cols; c++) {
420
476
  const key = `${row}:${c}`;
421
477
  const cell = data.cells[key];
@@ -429,11 +485,16 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
429
485
  if (cell.rowspan && cell.rowspan > 1) {
430
486
  attrs.push(`rowspan="${cell.rowspan}"`);
431
487
  }
432
- if (data.colAligns) {
433
- const align = data.colAligns[c];
434
- if (align && align !== "left") {
435
- attrs.push(`style="text-align: ${align}"`);
436
- }
488
+ const colDefault = data.colAligns?.[c] ?? "left";
489
+ const effectiveAlign = resolveCellHorizontalAlign(cell, c, data.colAligns);
490
+ let alignStyle = null;
491
+ if (effectiveAlign !== "left") {
492
+ alignStyle = `text-align: ${effectiveAlign}`;
493
+ } else if (cell.align === "left" && colDefault !== "left") {
494
+ alignStyle = "text-align: left";
495
+ }
496
+ if (alignStyle) {
497
+ attrs.push(`style="${alignStyle}"`);
437
498
  }
438
499
  const attrStr = attrs.length > 0 ? " " + attrs.join(" ") : "";
439
500
  let content = "";
@@ -449,6 +510,12 @@ function isGfmCompatible(data) {
449
510
  if (data.colWidths && data.colWidths.some((w) => w > 0)) {
450
511
  return false;
451
512
  }
513
+ if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
514
+ return false;
515
+ }
516
+ for (const cell of Object.values(data.cells)) {
517
+ if (cell !== null && cell.align !== void 0) return false;
518
+ }
452
519
  for (const cell of Object.values(data.cells)) {
453
520
  if (cell === null) return false;
454
521
  if (cell.colspan && cell.colspan > 1) return false;
@@ -558,15 +625,62 @@ function extractColWidths(table) {
558
625
  }
559
626
  return void 0;
560
627
  }
628
+ function extractRowHeightPx(row) {
629
+ const style = row.getAttribute("style") || "";
630
+ const heightMatch = style.match(/(?:^|;)\s*height:\s*([\d.]+)px/i);
631
+ if (heightMatch?.[1]) {
632
+ const n = parseFloat(heightMatch[1]);
633
+ if (Number.isFinite(n) && n > 0) return n;
634
+ }
635
+ const minHeightMatch = style.match(/(?:^|;)\s*min-height:\s*([\d.]+)px/i);
636
+ if (minHeightMatch?.[1]) {
637
+ const n = parseFloat(minHeightMatch[1]);
638
+ if (Number.isFinite(n) && n > 0) return n;
639
+ }
640
+ const heightAttr = row.getAttribute("height");
641
+ if (heightAttr) {
642
+ const n = parseFloat(heightAttr);
643
+ if (Number.isFinite(n) && n > 0) return n;
644
+ }
645
+ return null;
646
+ }
647
+ function extractRowHeights(rows) {
648
+ const heights = [];
649
+ let any = false;
650
+ for (const row of rows) {
651
+ const h = extractRowHeightPx(row);
652
+ if (h != null) {
653
+ heights.push(h);
654
+ any = true;
655
+ } else {
656
+ heights.push(0);
657
+ }
658
+ }
659
+ return any ? heights : void 0;
660
+ }
561
661
  function extractCellAlign(cell) {
562
- const textAlign = cell.style?.textAlign || cell.style?.getPropertyValue?.("text-align");
563
- if (textAlign === "left" || textAlign === "center" || textAlign === "right") {
564
- return textAlign;
662
+ const direct = extractInlineHorizontalAlign(cell);
663
+ if (direct) return direct;
664
+ const children = cell.childNodes;
665
+ for (let i = 0; i < children.length; i++) {
666
+ const child = children[i];
667
+ if (!child || !isElement(child)) continue;
668
+ const tag = child.tagName.toLowerCase();
669
+ if (tag === "p" || tag === "div") {
670
+ const align = extractInlineHorizontalAlign(child);
671
+ if (align) return align;
672
+ }
565
673
  }
566
- const style = cell.getAttribute("style") || "";
567
- const match = style.match(/text-align:\s*(left|center|right)/);
674
+ return null;
675
+ }
676
+ function extractInlineHorizontalAlign(element) {
677
+ const textAlign = element.style?.textAlign || element.style?.getPropertyValue?.("text-align");
678
+ const fromStyle = parseHorizontalAlign(textAlign);
679
+ if (fromStyle) return fromStyle;
680
+ const style = element.getAttribute("style") || "";
681
+ const match = style.match(/text-align:\s*(left|center|right|justify)/i);
568
682
  if (match?.[1]) {
569
- return match[1];
683
+ return parseHorizontalAlign(match[1]);
570
684
  }
571
685
  return null;
572
686
  }
@@ -575,6 +689,7 @@ function parseTableElement(table, context) {
575
689
  if (rows.length === 0) return null;
576
690
  const cells = {};
577
691
  const colAligns = [];
692
+ const rawAligns = {};
578
693
  let maxCol = 0;
579
694
  let firstRowProcessed = false;
580
695
  const occupied = /* @__PURE__ */ new Set();
@@ -603,10 +718,20 @@ function parseTableElement(table, context) {
603
718
  } else {
604
719
  ops = [{ insert: "\n" }];
605
720
  }
606
- const cellData = { ops };
721
+ let cellData = { ops };
607
722
  if (colspan > 1) cellData.colspan = colspan;
608
723
  if (rowspan > 1) cellData.rowspan = rowspan;
609
- cells[`${rowIdx}:${colIdx}`] = cellData;
724
+ cellData = promoteAlignFromCellOps(cellData);
725
+ const htmlAlign = extractCellAlign(cell);
726
+ const effectiveAlign = htmlAlign ?? cellData.align ?? "left";
727
+ if (cellData.align !== void 0) {
728
+ const withoutAlign = { ...cellData };
729
+ delete withoutAlign.align;
730
+ cellData = withoutAlign;
731
+ }
732
+ const cellKey = `${rowIdx}:${colIdx}`;
733
+ cells[cellKey] = cellData;
734
+ rawAligns[cellKey] = effectiveAlign;
610
735
  for (let dr = 0; dr < rowspan; dr++) {
611
736
  for (let dc = 0; dc < colspan; dc++) {
612
737
  if (dr === 0 && dc === 0) continue;
@@ -620,8 +745,9 @@ function parseTableElement(table, context) {
620
745
  }
621
746
  if (!firstRowProcessed) {
622
747
  const align = extractCellAlign(cell);
748
+ const colAlign = align === "center" || align === "right" ? align : null;
623
749
  for (let dc = 0; dc < colspan; dc++) {
624
- colAligns.push(align);
750
+ colAligns.push(colAlign);
625
751
  }
626
752
  }
627
753
  const cellEndCol = colIdx + colspan - 1;
@@ -657,11 +783,31 @@ function parseTableElement(table, context) {
657
783
  if (colWidths.length > totalCols) colWidths.length = totalCols;
658
784
  result.colWidths = colWidths;
659
785
  }
786
+ const rowHeights = extractRowHeights(rows);
787
+ if (rowHeights) {
788
+ while (rowHeights.length < totalRows) rowHeights.push(0);
789
+ if (rowHeights.length > totalRows) rowHeights.length = totalRows;
790
+ if (rowHeights.some((h) => h > 0)) {
791
+ result.rowHeights = rowHeights;
792
+ }
793
+ }
660
794
  if (colAligns.length > 0 && colAligns.some((a) => a !== null)) {
661
795
  while (colAligns.length < totalCols) colAligns.push(null);
662
796
  if (colAligns.length > totalCols) colAligns.length = totalCols;
663
797
  result.colAligns = colAligns;
664
798
  }
799
+ for (let r = 0; r < totalRows; r++) {
800
+ for (let c = 0; c < totalCols; c++) {
801
+ const key = `${r}:${c}`;
802
+ const cell = result.cells[key];
803
+ if (cell == null) continue;
804
+ const colDefault = result.colAligns?.[c] ?? "left";
805
+ const effective = rawAligns[key] ?? "left";
806
+ if (effective !== colDefault) {
807
+ result.cells[key] = { ...cell, align: effective };
808
+ }
809
+ }
810
+ }
665
811
  return result;
666
812
  }
667
813
  var tableBlockHandler = {
@@ -704,6 +850,14 @@ var tableBlockHandler = {
704
850
  if (typeof w !== "number" || w < 0) return false;
705
851
  }
706
852
  }
853
+ if (data.rowHeights !== void 0) {
854
+ if (!Array.isArray(data.rowHeights) || data.rowHeights.length !== rows) {
855
+ return false;
856
+ }
857
+ for (const h of data.rowHeights) {
858
+ if (typeof h !== "number" || h < 0) return false;
859
+ }
860
+ }
707
861
  if (data.colAligns !== void 0) {
708
862
  if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
709
863
  return false;
@@ -773,8 +927,11 @@ var tableBlockHandler = {
773
927
  for (const [key, cell] of Object.entries(data.cells)) {
774
928
  if (cell !== null) {
775
929
  const normalized = normalizeDelta(new Delta2(cell.ops), registry);
776
- if (normalized.ops !== cell.ops) {
777
- newCells[key] = { ...cell, ops: normalized.ops };
930
+ const promoted = promoteAlignFromCellOps(
931
+ normalized.ops !== cell.ops ? { ...cell, ops: normalized.ops } : cell
932
+ );
933
+ if (promoted.ops !== cell.ops || promoted.align !== cell.align) {
934
+ newCells[key] = promoted;
778
935
  changed = true;
779
936
  } else {
780
937
  newCells[key] = cell;
@@ -810,7 +967,7 @@ var tableBlockHandler = {
810
967
  };
811
968
 
812
969
  // src/schema/blocks/footnotes.ts
813
- import { isTextInsert, Delta as Delta3 } from "@scrider/delta";
970
+ import { isTextInsert as isTextInsert2, Delta as Delta3 } from "@scrider/delta";
814
971
  function isValidNoteData(note) {
815
972
  return typeof note === "object" && note !== null && Array.isArray(note.ops) && note.ops.length > 0;
816
973
  }
@@ -895,11 +1052,11 @@ var footnotesBlockHandler = {
895
1052
  if (context.parseElement) {
896
1053
  ops = context.parseElement(li);
897
1054
  ops = ops.map((op) => {
898
- if (isTextInsert(op) && op.insert.includes("\u21A9")) {
1055
+ if (isTextInsert2(op) && op.insert.includes("\u21A9")) {
899
1056
  return { ...op, insert: op.insert.replace(/\u21a9/g, "") };
900
1057
  }
901
1058
  return op;
902
- }).filter((op) => !isTextInsert(op) || op.insert !== "");
1059
+ }).filter((op) => !isTextInsert2(op) || op.insert !== "");
903
1060
  if (ops.length === 0) {
904
1061
  ops = [{ insert: "\n" }];
905
1062
  }
@@ -2652,7 +2809,7 @@ function isAdapterAvailable() {
2652
2809
  }
2653
2810
 
2654
2811
  // src/conversion/html/delta-to-html.ts
2655
- import { Delta as Delta7, isInsert as isInsert3, isEmbedInsert as isEmbedInsert2 } from "@scrider/delta";
2812
+ import { Delta as Delta7, isInsert as isInsert4, isEmbedInsert as isEmbedInsert2 } from "@scrider/delta";
2656
2813
 
2657
2814
  // src/conversion/utils/slugify.ts
2658
2815
  function slugify(text) {
@@ -2876,9 +3033,9 @@ function buildTableCellStyleAttr(params) {
2876
3033
  }
2877
3034
 
2878
3035
  // src/conversion/markdown/table-region.ts
2879
- import { isInsert as isInsert2, isTextInsert as isTextInsert2 } from "@scrider/delta";
3036
+ import { isInsert as isInsert3, isTextInsert as isTextInsert3 } from "@scrider/delta";
2880
3037
  function isTableNewlineOp(op) {
2881
- if (!op || !isInsert2(op) || !isTextInsert2(op)) return false;
3038
+ if (!op || !isInsert3(op) || !isTextInsert3(op)) return false;
2882
3039
  if (!op.insert.includes("\n")) return false;
2883
3040
  return !!op.attributes && "table-row" in op.attributes;
2884
3041
  }
@@ -2889,7 +3046,7 @@ function tableCellCoordsFromAttributes(attrs) {
2889
3046
  return { row: attrs["table-row"], col: attrs["table-col"] };
2890
3047
  }
2891
3048
  function tableCellCoordsFromOp(op) {
2892
- if (!isTableNewlineOp(op) || !isTextInsert2(op)) return null;
3049
+ if (!isTableNewlineOp(op) || !isTextInsert3(op)) return null;
2893
3050
  return tableCellCoordsFromAttributes(op.attributes);
2894
3051
  }
2895
3052
  function isAdjacentSimpleTableGridBoundary(prev, next) {
@@ -2916,8 +3073,8 @@ function extractTableRegion(ops, hintOpIdx) {
2916
3073
  let probeIdx = -1;
2917
3074
  for (let i = hintOpIdx; i < ops.length; i++) {
2918
3075
  const op = ops[i];
2919
- if (!op || !isInsert2(op)) continue;
2920
- if (isTextInsert2(op) && op.insert.includes("\n")) {
3076
+ if (!op || !isInsert3(op)) continue;
3077
+ if (isTextInsert3(op) && op.insert.includes("\n")) {
2921
3078
  probeIdx = i;
2922
3079
  break;
2923
3080
  }
@@ -2930,8 +3087,8 @@ function extractTableRegion(ops, hintOpIdx) {
2930
3087
  let prevCoords = probeCoords;
2931
3088
  for (let i = probeIdx + 1; i < ops.length; i++) {
2932
3089
  const op = ops[i];
2933
- if (!op || !isInsert2(op)) break;
2934
- if (!isTextInsert2(op) || !op.insert.includes("\n")) continue;
3090
+ if (!op || !isInsert3(op)) break;
3091
+ if (!isTextInsert3(op) || !op.insert.includes("\n")) continue;
2935
3092
  if (isTableNewlineOp(op)) {
2936
3093
  const coords = tableCellCoordsFromOp(op);
2937
3094
  if (isAdjacentSimpleTableGridBoundary(prevCoords, coords)) break;
@@ -2945,11 +3102,11 @@ function extractTableRegion(ops, hintOpIdx) {
2945
3102
  let nextCoords = probeCoords;
2946
3103
  for (let i = probeIdx - 1; i >= 0; i--) {
2947
3104
  const op = ops[i];
2948
- if (!op || !isInsert2(op)) {
3105
+ if (!op || !isInsert3(op)) {
2949
3106
  startOpIdx = i + 1;
2950
3107
  break;
2951
3108
  }
2952
- if (!isTextInsert2(op) || !op.insert.includes("\n")) continue;
3109
+ if (!isTextInsert3(op) || !op.insert.includes("\n")) continue;
2953
3110
  if (isTableNewlineOp(op)) {
2954
3111
  const coords = tableCellCoordsFromOp(op);
2955
3112
  if (isAdjacentSimpleTableGridBoundary(coords, nextCoords)) {
@@ -3095,7 +3252,7 @@ function splitIntoLines(delta) {
3095
3252
  const lines = [];
3096
3253
  let currentOps = [];
3097
3254
  for (const op of delta.ops) {
3098
- if (!isInsert3(op)) continue;
3255
+ if (!isInsert4(op)) continue;
3099
3256
  if (isEmbedInsert2(op)) {
3100
3257
  currentOps.push(op);
3101
3258
  continue;
@@ -3414,7 +3571,7 @@ function getBlockStyleAttribute(tag, attributes, resolvedDocumentPresentation) {
3414
3571
  function extractPlainText(ops) {
3415
3572
  let text = "";
3416
3573
  for (const op of ops) {
3417
- if (isInsert3(op) && typeof op.insert === "string") {
3574
+ if (isInsert4(op) && typeof op.insert === "string") {
3418
3575
  text += op.insert;
3419
3576
  }
3420
3577
  }
@@ -3423,7 +3580,7 @@ function extractPlainText(ops) {
3423
3580
  function renderLineContent(ops, embedRenderers, blockHandlers, options) {
3424
3581
  let html = "";
3425
3582
  for (const op of ops) {
3426
- if (!isInsert3(op)) continue;
3583
+ if (!isInsert4(op)) continue;
3427
3584
  if (isEmbedInsert2(op)) {
3428
3585
  html += renderEmbed(
3429
3586
  op.insert,
@@ -4221,7 +4378,7 @@ var DEFAULT_TAG_HANDLERS = {
4221
4378
  };
4222
4379
 
4223
4380
  // src/conversion/markdown/delta-to-markdown.ts
4224
- import { Delta as Delta9, isInsert as isInsert4, isTextInsert as isTextInsert3, isEmbedInsert as isEmbedInsert3 } from "@scrider/delta";
4381
+ import { Delta as Delta9, isInsert as isInsert5, isTextInsert as isTextInsert4, isEmbedInsert as isEmbedInsert3 } from "@scrider/delta";
4225
4382
 
4226
4383
  // src/conversion/markdown/config.ts
4227
4384
  var MARKDOWN_ESCAPE_CHARS = /[\\`*_[\]<>#]/g;
@@ -4414,9 +4571,9 @@ function splitIntoLines2(ops) {
4414
4571
  const lines = [];
4415
4572
  let currentOps = [];
4416
4573
  for (const op of ops) {
4417
- if (!isInsert4(op)) continue;
4574
+ if (!isInsert5(op)) continue;
4418
4575
  const opAttrs = op.attributes ?? {};
4419
- if (isTextInsert3(op)) {
4576
+ if (isTextInsert4(op)) {
4420
4577
  const text = op.insert;
4421
4578
  const parts = text.split("\n");
4422
4579
  for (let i = 0; i < parts.length; i++) {
@@ -4616,7 +4773,7 @@ function renderLineContent2(ops, embedRenderers, inCodeBlock, useLatexDelimiters
4616
4773
  let result = "";
4617
4774
  for (const op of ops) {
4618
4775
  const attrs = op.attributes;
4619
- if (isTextInsert3(op)) {
4776
+ if (isTextInsert4(op)) {
4620
4777
  const text = op.insert;
4621
4778
  if (inCodeBlock) {
4622
4779
  result += text;
@@ -4845,12 +5002,12 @@ function renderBlockFormat(content, attributes, orderedIndex, _strict) {
4845
5002
  }
4846
5003
 
4847
5004
  // src/conversion/markdown/markdown-to-delta.ts
4848
- import { Delta as Delta10, isInsert as isInsert6 } from "@scrider/delta";
5005
+ import { Delta as Delta10, isInsert as isInsert7 } from "@scrider/delta";
4849
5006
 
4850
5007
  // src/conversion/markdown/table-header-normalize.ts
4851
- import { isInsert as isInsert5 } from "@scrider/delta";
5008
+ import { isInsert as isInsert6 } from "@scrider/delta";
4852
5009
  function isTableCellTerminator(op) {
4853
- return isInsert5(op) && typeof op.insert === "string" && op.insert === "\n" && op.attributes !== void 0 && typeof op.attributes["table-row"] === "number";
5010
+ return isInsert6(op) && typeof op.insert === "string" && op.insert === "\n" && op.attributes !== void 0 && typeof op.attributes["table-row"] === "number";
4854
5011
  }
4855
5012
  function normalizeSyntheticEmptyHeaderRow(ops) {
4856
5013
  const ends = [];
@@ -4859,7 +5016,7 @@ function normalizeSyntheticEmptyHeaderRow(ops) {
4859
5016
  let currentTextOps = [];
4860
5017
  for (let i = 0; i < ops.length; i++) {
4861
5018
  const op = ops[i];
4862
- if (!isInsert5(op)) continue;
5019
+ if (!isInsert6(op)) continue;
4863
5020
  if (typeof op.insert === "string" && op.insert !== "\n") {
4864
5021
  buf += op.insert;
4865
5022
  currentTextOps.push(i);
@@ -4910,7 +5067,7 @@ function normalizeHeaderDashPlaceholders(ops) {
4910
5067
  let currentTextOps = [];
4911
5068
  for (let i = 0; i < ops.length; i++) {
4912
5069
  const op = ops[i];
4913
- if (!isInsert5(op)) continue;
5070
+ if (!isInsert6(op)) continue;
4914
5071
  if (typeof op.insert === "string" && op.insert !== "\n") {
4915
5072
  buf += op.insert;
4916
5073
  currentTextOps.push(i);
@@ -4939,7 +5096,7 @@ function normalizeHeaderDashPlaceholders(ops) {
4939
5096
  out.push(op);
4940
5097
  continue;
4941
5098
  }
4942
- if (!isInsert5(op)) {
5099
+ if (!isInsert6(op)) {
4943
5100
  out.push(op);
4944
5101
  continue;
4945
5102
  }
@@ -4960,7 +5117,7 @@ function normalizeImportedTableOps(ops) {
4960
5117
  const ends = [];
4961
5118
  let buf = "";
4962
5119
  for (const op of ops) {
4963
- if (!isInsert5(op)) continue;
5120
+ if (!isInsert6(op)) continue;
4964
5121
  if (typeof op.insert === "string" && op.insert !== "\n") {
4965
5122
  buf += op.insert;
4966
5123
  continue;
@@ -5496,7 +5653,7 @@ function astToDelta(tree, customHandlers, mathBlock, mermaidBlock, plantumlBlock
5496
5653
  }
5497
5654
  const tableOps = delta.ops.splice(tableStart);
5498
5655
  for (const op of normalizeImportedTableOps(tableOps)) {
5499
- if (!isInsert6(op)) continue;
5656
+ if (!isInsert7(op)) continue;
5500
5657
  if (op.attributes && Object.keys(op.attributes).length > 0) {
5501
5658
  delta.insert(op.insert, op.attributes);
5502
5659
  } else {