feat(CompareBoard): add measureRoleHeight via Pretext line count

This commit is contained in:
Ilia Mashkov
2026-06-24 13:49:23 +03:00
parent 0e9288c295
commit 2c3d88c81f
2 changed files with 78 additions and 0 deletions
@@ -0,0 +1,32 @@
import {
describe,
expect,
it,
vi,
} from 'vitest';
import { measureRoleHeight } from './measureFrameHeight';
describe('measureRoleHeight', () => {
it('multiplies pretext line count by sizePx*lineHeight', () => {
const layout = vi.fn().mockReturnValue({ lineCount: 3, height: 0 });
const prepared = {} as never;
// 3 lines * 20px * 1.5 = 90
expect(measureRoleHeight({ prepared, maxWidth: 600, sizePx: 20, lineHeight: 1.5 }, layout)).toBe(90);
});
it('passes width and pixel line-height into pretext layout', () => {
const layout = vi.fn().mockReturnValue({ lineCount: 1, height: 0 });
measureRoleHeight({ prepared: {} as never, maxWidth: 600, sizePx: 16, lineHeight: 1.25 }, layout);
expect(layout).toHaveBeenCalledWith(expect.anything(), 600, 16 * 1.25);
});
it('returns 0 when the text lays out to zero lines (empty specimen)', () => {
const layout = vi.fn().mockReturnValue({ lineCount: 0, height: 0 });
expect(measureRoleHeight({ prepared: {} as never, maxWidth: 600, sizePx: 16, lineHeight: 1.5 }, layout))
.toBe(0);
});
it('handles fractional sizes and line-heights without rounding', () => {
const layout = vi.fn().mockReturnValue({ lineCount: 2, height: 0 });
// 2 * 15.5 * 1.4 = 43.4
expect(measureRoleHeight({ prepared: {} as never, maxWidth: 320, sizePx: 15.5, lineHeight: 1.4 }, layout))
.toBeCloseTo(43.4);
});
});
@@ -0,0 +1,46 @@
import {
type PreparedText,
layout as pretextLayout,
} from '@chenglou/pretext';
/**
* Inputs for measuring one role block's rendered height.
*/
export interface RoleHeightInput {
/**
* Pretext-prepared specimen text for this role+font.
*/
prepared: PreparedText;
/**
* Available width in px (the focal frame's content width).
*/
maxWidth: number;
/**
* Resolved font-size in px.
*/
sizePx: number;
/**
* Unitless line-height multiplier.
*/
lineHeight: number;
}
/**
* Height in px of a role's text block at the given width, from Pretext's
* pure-arithmetic line count.
*
* Height is `lineCount * sizePx * lineHeight` rather than Pretext's own
* `height` so it tracks the CSS box model exactly (line-height as a multiple of
* font-size), keeping measurement and render in lockstep — the zero-shift
* invariant.
*
* @param input - Prepared text plus width and resolved type metrics.
* @param layout - Pretext layout fn; injectable for unit tests, defaults to
* `@chenglou/pretext`'s `layout`.
* @returns The block height in px.
*/
export function measureRoleHeight(input: RoleHeightInput, layout = pretextLayout): number {
const { prepared, maxWidth, sizePx, lineHeight } = input;
const { lineCount } = layout(prepared, maxWidth, sizePx * lineHeight);
return lineCount * sizePx * lineHeight;
}