Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions packages/layout-engine/layout-bridge/src/position-hit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ import type {
ParagraphBlock,
ParagraphMeasure,
} from '@superdoc/contracts';
import { adjustAvailableWidthForTextIndent, computeLinePmRange, getFirstLineIndentOffset } from '@superdoc/contracts';
import {
adjustAvailableWidthForTextIndent,
computeLinePmRange,
getFirstLineIndentOffset,
getParagraphInlineDirection,
} from '@superdoc/contracts';
import { charOffsetToPm, findCharacterAtX } from './text-measurement.js';
import type { PageGeometryHelper } from './page-geometry-helper.js';

Expand Down Expand Up @@ -120,26 +125,9 @@ export const getAtomicPmRange = (fragment: AtomicFragment, block: FlowBlock): {

export const isRtlBlock = (block: FlowBlock): boolean => {
if (block.kind !== 'paragraph') return false;
const attrs = block.attrs as Record<string, unknown> | undefined;
if (!attrs) return false;
// AIDEV-NOTE: The typed directionContext.inlineDirection (SD-2776) is the source of
// truth for paragraph inline direction. Check the value, not the key — `inlineDirection`
// can be `undefined` per the resolver contract (no explicit w:bidi anywhere in the
// cascade), and we should fall through to the legacy field in that case.
// Do NOT consult attrs.textDirection here: that's writing-mode (ECMA §17.18.93,
// values lrTb/tbRl/btLr/lrTbV/tbRlV/tbLrV) which is a separate axis from inline RTL.
const directionContext = attrs.directionContext as { inlineDirection?: string } | undefined;
if (directionContext?.inlineDirection != null) {
return directionContext.inlineDirection === 'rtl';
}
// AIDEV-NOTE: compat-fallback — `attrs.direction` / `attrs.dir` are the legacy scalar
// duplicates of `directionContext.inlineDirection`. Retire once SD-2778 collapses
// them on `ParagraphAttrs`.
const directionAttr = attrs.direction ?? attrs.dir;
if (typeof directionAttr === 'string' && directionAttr.toLowerCase() === 'rtl') {
return true;
}
return false;
return getParagraphInlineDirection(block.attrs) === 'rtl';
Comment thread
caio-pizzol marked this conversation as resolved.
};

export const determineColumn = (layout: Layout, fragmentX: number): number => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,13 @@ describe('isRtlBlock', () => {
),
).toBe(true);
});

// SD-2778: switching to getParagraphInlineDirection is strictly broader on
// fallback than the prior inline read. Specifically, the helper picks up
// paragraphProperties.rightToLeft when neither directionContext nor the legacy
// scalar field is present. Pin that case so the broader fallback is intentional.
it('falls back to paragraphProperties.rightToLeft when no other direction signal is present', () => {
expect(isRtlBlock(paragraph({ paragraphProperties: { rightToLeft: true } }))).toBe(true);
expect(isRtlBlock(paragraph({ paragraphProperties: { rightToLeft: false } }))).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -1777,7 +1777,12 @@ describe('resolveLayout', () => {
id,
runs: [{ kind: 'text', text: 'RTL list item' }],
attrs: {
direction: 'rtl',
// SD-2778: use directionContext so this test only passes through the
// new helper-driven typed path. The pre-migration code read
// attrs.direction directly, so the prior `direction: 'rtl'` fixture
// would have passed against the old implementation too and didn't
// actually prove the migration.
directionContext: { inlineDirection: 'rtl', writingMode: 'horizontal-tb' },
indent: { right, hanging: -24 },
wordLayout: {
marker: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
ResolvedDropCapItem,
ResolvedListMarkerItem,
} from '@superdoc/contracts';
import { adjustAvailableWidthForTextIndent } from '@superdoc/contracts';
import { adjustAvailableWidthForTextIndent, getParagraphInlineDirection } from '@superdoc/contracts';
import {
isMinimalWordLayout,
resolveListMarkerGeometry,
Expand Down Expand Up @@ -93,7 +93,7 @@ export function resolveParagraphContent(
const paraIndent = (block.attrs as ParagraphAttrs | undefined)?.indent;
const paraIndentLeft = paraIndent?.left ?? 0;
const paraIndentRight = paraIndent?.right ?? 0;
const isRtl = (block.attrs as ParagraphAttrs | undefined)?.direction === 'rtl';
const isRtl = getParagraphInlineDirection(block.attrs) === 'rtl';
const {
anchorIndentPx: paraMarkerAnchorIndent,
firstLinePx: markerFirstLine,
Expand Down
Loading