Refactor/reacrhitecture to fsd+ #49
@@ -9,43 +9,6 @@ import {
|
||||
*/
|
||||
const DEFAULT_RENDER_SIZE_PX = 16;
|
||||
|
||||
/**
|
||||
* 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[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts pretext's runtime shape. The public TS type exposes only `segments`;
|
||||
* the numeric arrays exist at runtime but are absent from the published signature.
|
||||
* Centralizing the cast keeps the engine body free of `as any`.
|
||||
*/
|
||||
function asPretextInternals(prepared: PreparedTextWithSegments): PretextInternals {
|
||||
return prepared as unknown as PretextInternals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-grapheme data computed during dual-font layout. Internal to the engine;
|
||||
* consumed by computeLineRenderModel to derive the per-frame render model.
|
||||
@@ -114,6 +77,10 @@ export interface ComparisonResult {
|
||||
* of font A and font B. This guarantees that both fonts wrap at exactly the same
|
||||
* positions, making side-by-side or slider comparison visually coherent.
|
||||
*
|
||||
* Relies on pretext's published structural fields on `PreparedTextWithSegments`
|
||||
* (`widths`, `breakableFitAdvances`, `lineEndFitAdvances`, `lineEndPaintAdvances`)
|
||||
* which are exposed via the `PreparedCore` intersection in `@chenglou/pretext@0.0.6`.
|
||||
*
|
||||
* **Two-level caching strategy**
|
||||
* 1. Font-change cache (`#preparedA`, `#preparedB`, `#unifiedPrepared`): rebuilt only
|
||||
* when `text`, `fontA`, or `fontB` changes. `prepareWithSegments` is expensive
|
||||
@@ -129,9 +96,9 @@ export class DualFontLayout {
|
||||
#segmenter: Intl.Segmenter;
|
||||
|
||||
// Cached prepared data
|
||||
#preparedA: PretextInternals | null = null;
|
||||
#preparedB: PretextInternals | null = null;
|
||||
#unifiedPrepared: PretextInternals | null = null;
|
||||
#preparedA: PreparedTextWithSegments | null = null;
|
||||
#preparedB: PreparedTextWithSegments | null = null;
|
||||
#unifiedPrepared: PreparedTextWithSegments | null = null;
|
||||
|
||||
#lastText = '';
|
||||
#lastFontA = '';
|
||||
@@ -192,8 +159,8 @@ export class DualFontLayout {
|
||||
|
||||
// 1. Prepare (or use cache)
|
||||
if (isFontChange) {
|
||||
this.#preparedA = asPretextInternals(prepareWithSegments(text, fontA));
|
||||
this.#preparedB = asPretextInternals(prepareWithSegments(text, fontB));
|
||||
this.#preparedA = prepareWithSegments(text, fontA);
|
||||
this.#preparedB = prepareWithSegments(text, fontB);
|
||||
this.#unifiedPrepared = this.#createUnifiedPrepared(this.#preparedA, this.#preparedB, spacingPx);
|
||||
|
||||
this.#lastText = text;
|
||||
@@ -207,13 +174,7 @@ export class DualFontLayout {
|
||||
return { lines: [], totalHeight: 0 };
|
||||
}
|
||||
|
||||
// pretext's `layoutWithLines` is typed against its public surface; pass the
|
||||
// runtime-internal shape through with one boundary cast.
|
||||
const { lines, height } = layoutWithLines(
|
||||
this.#unifiedPrepared as unknown as PreparedTextWithSegments,
|
||||
width,
|
||||
lineHeight,
|
||||
);
|
||||
const { lines, height } = layoutWithLines(this.#unifiedPrepared, width, lineHeight);
|
||||
|
||||
// 3. Map results back to both fonts
|
||||
const preparedA = this.#preparedA;
|
||||
@@ -284,11 +245,11 @@ export class DualFontLayout {
|
||||
* across both fonts, with `spacingPx` added to model letter-spacing.
|
||||
*/
|
||||
#createUnifiedPrepared(
|
||||
a: PretextInternals,
|
||||
b: PretextInternals,
|
||||
a: PreparedTextWithSegments,
|
||||
b: PreparedTextWithSegments,
|
||||
spacingPx: number = 0,
|
||||
): PretextInternals {
|
||||
const unified: PretextInternals = { ...a };
|
||||
): PreparedTextWithSegments {
|
||||
const unified: PreparedTextWithSegments = { ...a };
|
||||
|
||||
unified.widths = a.widths.map((w, i) => Math.max(w, b.widths[i]) + spacingPx);
|
||||
unified.lineEndFitAdvances = a.lineEndFitAdvances.map((w, i) =>
|
||||
|
||||
Reference in New Issue
Block a user