Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
d4b2a31
feat: add v3 translator for w:aliases
Jan 9, 2026
459abd3
feat: add v3 translator for w:name
Jan 9, 2026
3513972
feat: add v3 translator for w:basedOn
Jan 9, 2026
40b1714
feat: add v3 translator for w:link
Jan 9, 2026
7b64232
feat: add v3 translator for w:next
Jan 9, 2026
8a8a276
feat: register translators
Jan 9, 2026
101e8d0
refactor: use utility functions in w:hidden translator
Jan 9, 2026
6408661
feat: add v3 translator for w:autoRedefine
Jan 9, 2026
adf18ed
feat: add v3 translator for w:locked
Jan 9, 2026
0ffa6d6
feat: add v3 translator for w:personal
Jan 9, 2026
c799625
feat: add v3 translator for w:personalCompose
Jan 9, 2026
a62b5d2
feat: add v3 translator for w:personalReply
Jan 9, 2026
f000085
feat: add v3 translator for w:qFormat
Jan 9, 2026
4392906
feat: add v3 translator for w:semiHidden
Jan 9, 2026
79ecd58
feat: add v3 translator for w:unhideWhenUsed
Jan 9, 2026
c35c2ab
feat: register translators
Jan 9, 2026
f94bac8
feat: add v3 translator for w:rsid
Jan 9, 2026
e11a6a1
feat: add v3 translator for w:uiPriority
Jan 9, 2026
b3c829f
feat: register translators
Jan 9, 2026
95868a1
docs: fix typing
Jan 9, 2026
e76eb0c
feat: add support for attribute handlers in nested props translators
Jan 9, 2026
1dd1f34
feat: add v3 translator for w:tblStylePr
Jan 9, 2026
e6dd83d
feat: add v3 translator for w:style
Jan 9, 2026
24e1634
feat: add v3 translator for w:docDefaults
Jan 9, 2026
c6f8621
feat: add helper functions for encoding and decoding properties as map
Jan 9, 2026
063d59b
feat: add v3 translator for w:lsdException
Jan 10, 2026
32d9deb
feat: add v3 translator for w:latentStyles
Jan 10, 2026
761fed9
feat: add v3 translator for w:styles
Jan 9, 2026
e5dd808
feat: add v3 translator for w:start (child of w:lvl)
Jan 9, 2026
b352346
feat: add v3 translator for w:lvlRestart
Jan 9, 2026
bd0fdf2
feat: add v3 translator for w:lvlPicBulletId
Jan 9, 2026
53d6aae
feat: add v3 translator for w:isLgl
Jan 9, 2026
eeeee24
feat: add v3 translator for w:lvlJc
Jan 9, 2026
a772544
feat: add v3 translator for w:suff
Jan 9, 2026
5ac5241
feat: add v3 translator for w:lvlText
Jan 9, 2026
9b72a73
fix: circular import
Jan 9, 2026
d25eb9f
feat: add v3 translator for w:numFmt
Jan 9, 2026
e01c258
feat: add v3 translator for w:legacy
Jan 9, 2026
1e42538
feat: add v3 translator for w:lvl
Jan 9, 2026
5882bb4
feat: add v3 translator for w:startOverride
Jan 9, 2026
ea2324b
feat: add v3 translator for w:lvlOverride
Jan 9, 2026
e649ca5
feat: add v3 translator for w:abstractNumId
Jan 9, 2026
aaff722
feat: add v3 translator for w:num
Jan 9, 2026
432f7fd
feat: add v3 translator for w:nsid
Jan 9, 2026
c9bfac1
feat: add v3 translator for w:tmpl
Jan 9, 2026
1c9fb3c
feat: add v3 translator for w:styleLink
Jan 9, 2026
6207380
feat: add v3 translator for w:numStyleLink
Jan 9, 2026
87a7212
feat: add v3 translator for w:multiLevelType
Jan 9, 2026
31208f5
feat: add v3 translator for w:abstractNum
Jan 9, 2026
6b5db86
feat: add v3 translator fow w:numIdMacAtCleanup
Jan 10, 2026
5ab2510
feat: add v3 translator fow w:numbering
Jan 9, 2026
3b4aae9
feat: use v3 translator for linked styles and numbering
Jan 12, 2026
233103a
feat: add typing information for raw paragraph and run attrs
Jan 12, 2026
ae548a0
fix: rework list marker tab alignment
Jan 15, 2026
5c4d1f6
refactor: simplify list layout inputs and drop marker utils
Jan 15, 2026
6b1e91b
feat: pass translated styles/numbering into editor state
Jan 15, 2026
2243e2b
refactor: migrate paragraph/run resolution to translated styles
Jan 15, 2026
d6e0657
fix: typing fixes
Jan 16, 2026
ae156e3
chore: format
Jan 16, 2026
99da0c6
test: adjust unit tests
Jan 16, 2026
869caf6
fix: consider table style in style resolution
Jan 19, 2026
639ca22
test: add tests for style resolution
Jan 19, 2026
0086c22
fix: changes after rebase
Jan 19, 2026
c5c2d7d
fix: before and after auto spacing
Jan 19, 2026
777aebc
fix: support w:numStyleLink in style resolution
Jan 19, 2026
793438a
fix: dropCap normalization
Jan 19, 2026
68820c0
fix: pm-adapter build
Jan 19, 2026
3892b43
fix: handle missing docDefaults
Jan 19, 2026
1f93d3c
test: adjust existing tests
Jan 19, 2026
11dc076
fix: font resolution
Jan 20, 2026
2622169
fix: resolution of tab stops
Jan 20, 2026
e2cb8ed
fix: style resolution for page references
Jan 20, 2026
1cc62b0
test: adjust existing tests
Jan 20, 2026
4102f17
feat: add support for "decimalZero" format for lists
Jan 20, 2026
0a3b357
fix: apply marks before inline run properties
Jan 20, 2026
b7a6fc4
fix: translation of table style properties
Jan 21, 2026
743f16a
refactor: modify how table info is passed for style resolution
Jan 21, 2026
62e39cc
chore: remove unused code
Jan 21, 2026
af41d9b
fix: font family resolution
Jan 21, 2026
4bb0abc
fix: adjust table band size translators to parse integer
Jan 21, 2026
0c3fbc1
feat: implement style resolution for table cells
Jan 21, 2026
6e7447c
fix: normalization of underline value
Jan 21, 2026
0eb99f1
fix: font family resolution
Jan 21, 2026
e9d3e87
fix: numbering props resolution
Jan 21, 2026
22b973c
test: update e2e test screenshots
Jan 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions packages/layout-engine/contracts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1170,7 +1170,7 @@ export type WordLayoutConfig = {

export type ParagraphAttrs = {
styleId?: string;
alignment?: 'left' | 'center' | 'right' | 'justify' | 'both';
alignment?: 'left' | 'center' | 'right' | 'justify';
spacing?: ParagraphSpacing;
/**
* Indicates which spacing properties were explicitly set on the paragraph.
Expand All @@ -1197,14 +1197,15 @@ export type ParagraphAttrs = {
*/
dropCapDescriptor?: DropCapDescriptor;
frame?: ParagraphFrame;
numberingProperties?: Record<string, unknown>;
numberingProperties?: { ilvl?: number; numId?: number } | null;
borders?: ParagraphBorders;
shading?: ParagraphShading;
tabs?: TabStop[];
decimalSeparator?: string;
tabIntervalTwips?: number;
keepNext?: boolean;
keepLines?: boolean;
pageBreakBefore?: boolean;
trackedChangesMode?: TrackedChangesMode;
trackedChangesEnabled?: boolean;
/** Marks an empty paragraph that only exists to carry section properties. */
Expand Down
6 changes: 3 additions & 3 deletions packages/layout-engine/layout-bridge/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ export function clickToPosition(
const markerWidth = fragment.markerWidth ?? measure.marker?.markerWidth ?? 0;
const isListItem = markerWidth > 0;
const paraAlignment = block.attrs?.alignment;
const isJustified = paraAlignment === 'justify' || paraAlignment === 'both';
const isJustified = paraAlignment === 'justify';
const alignmentOverride = isListItem && !isJustified ? 'left' : undefined;

const pos = mapPointToPm(block, line, pageRelativePoint.x - fragment.x, isRTL, availableWidth, alignmentOverride);
Expand Down Expand Up @@ -1089,7 +1089,7 @@ export function clickToPosition(
const cellMarkerWidth = cellMeasure.marker?.markerWidth ?? 0;
const isListItem = cellMarkerWidth > 0;
const cellAlignment = cellBlock.attrs?.alignment;
const isJustified = cellAlignment === 'justify' || cellAlignment === 'both';
const isJustified = cellAlignment === 'justify';
const alignmentOverride = isListItem && !isJustified ? 'left' : undefined;

const pos = mapPointToPm(cellBlock, line, localX, isRTL, availableWidth, alignmentOverride);
Expand Down Expand Up @@ -1412,7 +1412,7 @@ export function selectionToRects(
// List items use textAlign: 'left' in the DOM for non-justify alignments.
// For justify, we don't override so justify selection rectangles are calculated correctly.
const blockAlignment = block.attrs?.alignment;
const isJustified = blockAlignment === 'justify' || blockAlignment === 'both';
const isJustified = blockAlignment === 'justify';
const alignmentOverride = isListItemFlag && !isJustified ? 'left' : undefined;
const startX = mapPmToX(block, line, charOffsetFrom, fragment.width, alignmentOverride);
const endX = mapPmToX(block, line, charOffsetTo, fragment.width, alignmentOverride);
Expand Down
10 changes: 2 additions & 8 deletions packages/layout-engine/layout-bridge/src/remeasure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1079,13 +1079,7 @@ export function remeasureParagraph(
// Both values represent the same concept: where the first-line text begins after the marker/tab.
// IMPORTANT: Priority must match the painter (renderer.ts) which prefers marker.textStartX
// because it's consistent with marker.markerX positioning. Mismatched priority causes justify overflow.
const markerTextStartX = wordLayout?.marker?.textStartX;
const textStartPx =
typeof markerTextStartX === 'number' && Number.isFinite(markerTextStartX)
? markerTextStartX
: typeof wordLayout?.textStartPx === 'number' && Number.isFinite(wordLayout.textStartPx)
? wordLayout.textStartPx
: undefined;
const textStartPx = wordLayout?.textStartPx;
// Track measured marker text width for returning in measure.marker
let measuredMarkerTextWidth: number | undefined;
const resolvedTextStartPx = resolveListTextStartPx(
Expand Down Expand Up @@ -1319,7 +1313,7 @@ export function remeasureParagraph(
const marker = wordLayout?.marker;
const markerInfo = marker
? {
markerWidth: marker.markerBoxWidthPx ?? indentHanging ?? 0,
markerWidth: indentHanging ?? 0,
markerTextWidth: measuredMarkerTextWidth ?? 0,
indentLeft,
gutterWidth: marker.gutterWidthPx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@ describe('layoutParagraphBlock - remeasurement with list markers', () => {
expect(remeasureParagraph).toHaveBeenCalledWith(block, 150, 24);
});

it('uses fallback to markerBoxWidthPx when markerWidth is missing', () => {
it('uses markerWidth=0 fallback when markerWidth is missing', () => {
const remeasureParagraph = vi.fn((block, maxWidth, firstLineIndent) => {
// Should use markerBoxWidthPx (20) + gutterWidth (6)
expect(firstLineIndent).toBe(26);
// markerWidth defaults to 0 when the measure marker is present
expect(firstLineIndent).toBe(6);
return makeMeasure([{ width: 100, lineHeight: 20, maxWidth: 150 }]);
});

Expand All @@ -226,7 +226,7 @@ describe('layoutParagraphBlock - remeasurement with list markers', () => {

const measure = makeMeasure(
[{ width: 100, lineHeight: 20, maxWidth: 200 }],
{ gutterWidth: 6 }, // markerWidth is missing
{ gutterWidth: 6 }, // markerWidth is missing and defaults to 0
);

const ctx: ParagraphLayoutContext = {
Expand All @@ -242,7 +242,7 @@ describe('layoutParagraphBlock - remeasurement with list markers', () => {

layoutParagraphBlock(ctx);

expect(remeasureParagraph).toHaveBeenCalledWith(block, 150, 26);
expect(remeasureParagraph).toHaveBeenCalledWith(block, 150, 6);
});

it('uses fallback to 0 when both markerWidth and markerBoxWidthPx are missing', () => {
Expand Down
99 changes: 90 additions & 9 deletions packages/layout-engine/layout-engine/src/layout-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1439,13 +1439,48 @@ describe('layoutTableBlock', () => {
const measure = createMockTableMeasure(
[100, 100], // Two columns
[120], // Row height (max of cell heights)
[
// Row 0 - different line heights per cell
[20, 20, 20], // Cell 0: 3x20px lines
[40, 40, 40], // Cell 1: 3x40px lines
],
[[20, 20, 20]], // Row 0 defaults (applied to all cells)
);

if (measure.rows[0].cells[1]) {
measure.rows[0].cells[1].paragraph = {
kind: 'paragraph',
lines: [
{
fromRun: 0,
fromChar: 0,
toRun: 0,
toChar: 1,
width: 100,
ascent: 30,
descent: 10,
lineHeight: 40,
},
{
fromRun: 0,
fromChar: 0,
toRun: 0,
toChar: 1,
width: 100,
ascent: 30,
descent: 10,
lineHeight: 40,
},
{
fromRun: 0,
fromChar: 0,
toRun: 0,
toChar: 1,
width: 100,
ascent: 30,
descent: 10,
lineHeight: 40,
},
],
totalHeight: 120,
};
}

const fragments: TableFragment[] = [];
let pageCount = 0;
const mockPage = { fragments };
Expand Down Expand Up @@ -1583,12 +1618,58 @@ describe('layoutTableBlock', () => {
const measure = createMockTableMeasure(
[100, 100],
[80], // Row height (max of cells)
[
[20, 20], // Cell 0: 2 lines of 20px (total 40px)
[20, 20, 20, 20], // Cell 1: 4 lines of 20px (total 80px)
],
[[20, 20]], // Row 0 defaults (applied to all cells)
);

if (measure.rows[0].cells[1]) {
measure.rows[0].cells[1].paragraph = {
kind: 'paragraph',
lines: [
{
fromRun: 0,
fromChar: 0,
toRun: 0,
toChar: 1,
width: 100,
ascent: 15,
descent: 5,
lineHeight: 20,
},
{
fromRun: 0,
fromChar: 0,
toRun: 0,
toChar: 1,
width: 100,
ascent: 15,
descent: 5,
lineHeight: 20,
},
{
fromRun: 0,
fromChar: 0,
toRun: 0,
toChar: 1,
width: 100,
ascent: 15,
descent: 5,
lineHeight: 20,
},
{
fromRun: 0,
fromChar: 0,
toRun: 0,
toChar: 1,
width: 100,
ascent: 15,
descent: 5,
lineHeight: 20,
},
],
totalHeight: 80,
};
}

const fragments: TableFragment[] = [];
let cursorY = 0;
const mockPage = { fragments };
Expand Down
4 changes: 2 additions & 2 deletions packages/layout-engine/measuring/dom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2299,7 +2299,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
: LIST_MARKER_GAP;

// Marker box should match Word's box width when provided; otherwise fall back to glyph + gap.
const markerBoxWidth = Math.max(wordLayout.marker.markerBoxWidthPx ?? 0, glyphWidth + LIST_MARKER_GAP);
const markerBoxWidth = Math.max(0, glyphWidth + LIST_MARKER_GAP);

markerInfo = {
markerWidth: markerBoxWidth,
Expand Down Expand Up @@ -2971,7 +2971,7 @@ async function measureListBlock(block: ListBlock, constraints: MeasureConstraint
};
const { font: markerFont } = buildFontString(markerFontRun);
markerTextWidth = marker.markerText ? measureText(marker.markerText, markerFont, ctx) : 0;
markerWidth = marker.markerBoxWidthPx;
markerWidth = 0;
indentLeft = (wordLayout as WordParagraphLayoutOutput).indentLeftPx ?? 0;
} else {
// Fallback: legacy behavior for backwards compatibility
Expand Down
38 changes: 25 additions & 13 deletions packages/layout-engine/painters/dom/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ describe('DomPainter', () => {
y: 0,
width: 400,
markerWidth: 24,
markerTextWidth: 12,
},
],
},
Expand Down Expand Up @@ -860,6 +861,7 @@ describe('DomPainter', () => {
y: 0,
width: 400,
markerWidth: 24,
markerTextWidth: 12,
},
],
},
Expand All @@ -872,11 +874,11 @@ describe('DomPainter', () => {
const firstLine = mount.querySelector('.superdoc-line') as HTMLElement;
// Word-spacing is calculated based on available width AFTER accounting for marker position + inline width.
// Fragment has indent: { left: 48, hanging: 24 }, so markerStartPos = 48 - 24 = 24
// fragment.markerTextWidth is not set, so falls back to fragment.markerWidth = 24
// Text starts at: markerStartPos (24) + markerTextWidth (24) + space (~4px) = 52px
// availableWidth = 400 - 52 = 348
// slack = 348 - 180 = 168, wordSpacing = 168 / 5 = 33.6px
expect(firstLine.style.wordSpacing).toBe('33.6px');
// fragment.markerTextWidth is 12
// Text starts at: markerStartPos (24) + markerTextWidth (12) + space (4px) = 40px
// availableWidth = 400 - 40 = 360
// slack = 360 - 180 = 180, wordSpacing = 180 / 5 = 36px
expect(firstLine.style.wordSpacing).toBe('36px');

const suffix = firstLine.querySelector('.superdoc-marker-suffix-space') as HTMLElement;
expect(suffix).toBeTruthy();
Expand Down Expand Up @@ -944,6 +946,7 @@ describe('DomPainter', () => {
y: 0,
width: 400,
markerWidth: 24,
markerTextWidth: 12,
},
],
},
Expand Down Expand Up @@ -1855,6 +1858,7 @@ describe('DomPainter', () => {
y: 96,
width: 300,
markerWidth: 20,
markerTextWidth: 12,
},
],
},
Expand Down Expand Up @@ -1943,6 +1947,7 @@ describe('DomPainter', () => {
y: 120,
width: 300,
markerWidth: 24,
markerTextWidth: 12,
},
],
},
Expand Down Expand Up @@ -2028,6 +2033,7 @@ describe('DomPainter', () => {
y: 96,
width: 300,
markerWidth: 15,
markerTextWidth: 10,
},
],
},
Expand All @@ -2047,10 +2053,10 @@ describe('DomPainter', () => {

// Tab should reach implicit tab stop at indentLeft (48px)
// markerStartPos = paraIndentLeft - hanging = 48 - 24 = 24
// currentPos = markerStartPos + markerWidth = 24 + 15 = 39
// currentPos = markerStartPos + markerTextWidth = 24 + 10 = 34
// implicitTabStop = paraIndentLeft = 48
// tabWidth = 48 - 39 = 9
const expectedTabWidth = 9;
// tabWidth = 48 - 34 = 14
const expectedTabWidth = 14;
expect(tabEl.style.width).toBe(`${expectedTabWidth}px`);
});

Expand Down Expand Up @@ -2116,6 +2122,7 @@ describe('DomPainter', () => {
y: 96,
width: 300,
markerWidth: 45,
markerTextWidth: 40,
},
],
},
Expand All @@ -2135,11 +2142,11 @@ describe('DomPainter', () => {

// Marker extends past implicit tab stop, so advance to next default tab interval
// markerStartPos = paraIndentLeft - hanging = 24 - 12 = 12
// currentPos = markerStartPos + markerWidth = 12 + 45 = 57
// currentPos = markerStartPos + markerTextWidth = 12 + 40 = 52
// implicitTabStop = paraIndentLeft = 24
// tabWidth would be negative (24 - 57 = -33), so use default tab interval
// tabWidth = 48 - (57 % 48) = 48 - 9 = 39
const expectedTabWidth = 39;
// tabWidth = 48 - (52 % 48) = 48 - 4 = 44
const expectedTabWidth = 44;
expect(tabEl.style.width).toBe(`${expectedTabWidth}px`);
});

Expand Down Expand Up @@ -2208,6 +2215,7 @@ describe('DomPainter', () => {
width: 300,
markerWidth: 20,
markerGutter: 12,
markerTextWidth: 10,
},
],
},
Expand All @@ -2225,8 +2233,8 @@ describe('DomPainter', () => {
const tabEl = fragment.querySelector('.superdoc-tab') as HTMLElement;
expect(tabEl).toBeTruthy();

// For right-justified markers, use fragment.markerGutter
const expectedTabWidth = 12;
// For right-justified markers without firstLine, tab width uses hanging indent
const expectedTabWidth = 24;
expect(tabEl.style.width).toBe(`${expectedTabWidth}px`);
});

Expand Down Expand Up @@ -8662,6 +8670,7 @@ describe('applyRunDataAttributes', () => {
y: 24,
width: 300,
markerWidth: 15,
markerTextWidth: 10,
pmStart: 0,
pmEnd: 14,
},
Expand Down Expand Up @@ -8758,6 +8767,7 @@ describe('applyRunDataAttributes', () => {
y: 24,
width: 300,
markerWidth: 15,
markerTextWidth: 10,
pmStart: 0,
pmEnd: 4,
},
Expand Down Expand Up @@ -8849,6 +8859,7 @@ describe('applyRunDataAttributes', () => {
y: 24,
width: 300,
markerWidth: 15,
markerTextWidth: 10,
pmStart: 0,
pmEnd: 4,
},
Expand Down Expand Up @@ -8924,6 +8935,7 @@ describe('applyRunDataAttributes', () => {
y: 24,
width: 300,
markerWidth: 15,
markerTextWidth: 10,
pmStart: 0,
pmEnd: 4,
},
Expand Down
Loading
Loading