diff --git a/src/entities/Font/lib/dualFontLayout/DualFontLayout/DualFontLayout.ts b/src/entities/Font/lib/dualFontLayout/DualFontLayout/DualFontLayout.ts new file mode 100644 index 0000000..3451ec5 --- /dev/null +++ b/src/entities/Font/lib/dualFontLayout/DualFontLayout/DualFontLayout.ts @@ -0,0 +1,87 @@ +/** + * Internal shape of pretext's PreparedTextWithSegments — only `segments` is in + * pretext's public TS type; the numeric arrays exist at runtime but are not in + * the published signature. Verified against node_modules/@chenglou/pretext/src/layout.ts. + */ +interface PretextInternals { + /** + * Per-segment text. + */ + segments: string[]; + /** + * Per-segment full width in pixels. + */ + widths: number[]; + /** + * Per-segment per-grapheme advance widths, or null when the segment is a single grapheme. + */ + breakableFitAdvances: (number[] | null)[]; + /** + * Per-segment line-end fit advance. + */ + lineEndFitAdvances: number[]; + /** + * Per-segment line-end paint advance. + */ + lineEndPaintAdvances: number[]; +} + +/** + * Per-grapheme data computed during dual-font layout. Internal to the engine; + * consumed by computeLineRenderModel to derive the per-frame render model. + */ +export interface ComparisonChar { + /** + * Grapheme cluster (may be >1 code unit for emoji, combining marks). + */ + char: string; + /** + * X offset from line start in fontA, pixels. + */ + xA: number; + /** + * Advance width of this grapheme in fontA, pixels. + */ + widthA: number; + /** + * X offset from line start in fontB, pixels. + */ + xB: number; + /** + * Advance width of this grapheme in fontB, pixels. + */ + widthB: number; +} + +/** + * A single laid-out line. `chars` carries the per-grapheme data needed by + * computeLineRenderModel. Consumers should not iterate it directly. + */ +export interface ComparisonLine { + /** + * Full text of this line as returned by pretext. + */ + text: string; + /** + * Rendered width in pixels — maximum across fontA and fontB. + */ + width: number; + /** + * Per-grapheme metadata for both fonts. + */ + chars: ComparisonChar[]; +} + +/** + * Aggregated output of a dual-font layout pass. + */ +export interface ComparisonResult { + /** + * Per-line grapheme data. Empty when input text is empty. + */ + lines: ComparisonLine[]; + /** + * Total height in pixels. + */ + totalHeight: number; +} diff --git a/src/entities/Font/lib/dualFontLayout/computeLineRenderModel/computeLineRenderModel.test.ts b/src/entities/Font/lib/dualFontLayout/computeLineRenderModel/computeLineRenderModel.test.ts new file mode 100644 index 0000000..1d9575b --- /dev/null +++ b/src/entities/Font/lib/dualFontLayout/computeLineRenderModel/computeLineRenderModel.test.ts @@ -0,0 +1,5 @@ +import { describe } from 'vitest'; + +describe('computeLineRenderModel', () => { + // tests added in subsequent tasks +}); diff --git a/src/entities/Font/lib/dualFontLayout/computeLineRenderModel/computeLineRenderModel.ts b/src/entities/Font/lib/dualFontLayout/computeLineRenderModel/computeLineRenderModel.ts new file mode 100644 index 0000000..b179382 --- /dev/null +++ b/src/entities/Font/lib/dualFontLayout/computeLineRenderModel/computeLineRenderModel.ts @@ -0,0 +1,45 @@ +import type { + ComparisonChar, + ComparisonLine, +} from '../DualFontLayout/DualFontLayout'; + +/** + * Per-line render slice consumed by Line.svelte. The window is centered on the + * slider's split index and clamps at line boundaries. + */ +export interface LineRenderModel { + /** + * Chars before the window joined into a single string, rendered as one fontA text run. + */ + leftText: string; + /** + * Window chars — each rendered as its own Character element with crossfade slots. + */ + windowChars: Array<{ + /** + * Stable key for Svelte keyed each — survives slider movement within the same line. + */ + key: string; + /** + * Grapheme cluster to render. + */ + char: string; + /** + * True once the slider has crossed this char's threshold. + */ + isPast: boolean; + }>; + /** + * Chars after the window joined into a single string, rendered as one fontB text run. + */ + rightText: string; +} + +export function computeLineRenderModel( + line: ComparisonLine, + sliderPos: number, + containerWidth: number, + windowSize: number, +): LineRenderModel { + throw new Error('not implemented'); +} diff --git a/src/entities/Font/lib/dualFontLayout/index.ts b/src/entities/Font/lib/dualFontLayout/index.ts new file mode 100644 index 0000000..0c8da73 --- /dev/null +++ b/src/entities/Font/lib/dualFontLayout/index.ts @@ -0,0 +1,8 @@ +export type { + ComparisonLine, + ComparisonResult, +} from './DualFontLayout/DualFontLayout'; +export { + computeLineRenderModel, + type LineRenderModel, +} from './computeLineRenderModel/computeLineRenderModel';