refactor(comparison): switch to 3-section render model via DualFontLayout

Rewrite Line.svelte to render leftText / windowChars / rightText regions
from a LineRenderModel. Bulk regions render as native shaped text runs so
the browser applies kerning and ligatures; per-char DOM is reserved for
the N-char crossfade window straddling the slider.

Slim Character.svelte: drop the unused proximity prop and the redundant
font-size/font-weight/letter-spacing styles now inherited from the line
container.

Switch SliderArea.svelte to instantiate DualFontLayout and derive each
line's render model via computeLineRenderModel(line, sliderPos,
containerWidth, WINDOW_SIZE).
This commit is contained in:
Ilia Mashkov
2026-05-30 22:29:43 +03:00
parent 3e568685b3
commit 6153769317
3 changed files with 46 additions and 57 deletions
@@ -9,10 +9,12 @@
-->
<script lang="ts">
import {
CharacterComparisonEngine,
type ComparisonResult,
DualFontLayout,
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
computeLineRenderModel,
} from '$entities/Font';
import { TypographyMenu } from '$features/AdjustTypography';
import { typographySettingsStore } from '$features/AdjustTypography/model';
@@ -30,7 +32,6 @@ import {
getPretextFontString,
} from '../../lib';
import { comparisonStore } from '../../model';
import Character from '../Character/Character.svelte';
import Line from '../Line/Line.svelte';
import Thumb from '../Thumb/Thumb.svelte';
@@ -95,10 +96,15 @@ let isDragging = $state(false);
let isTypographyMenuOpen = $state(false);
let containerWidth = $state(0);
// New high-performance layout engine
const comparisonEngine = new CharacterComparisonEngine();
const layout = new DualFontLayout();
let layoutResult = $state<ReturnType<typeof comparisonEngine.layout>>({ lines: [], totalHeight: 0 });
let layoutResult = $state<ComparisonResult>({ lines: [], totalHeight: 0 });
/**
* N-window size for the per-char crossfade zone around the slider split.
* Tuned so chars complete their 100ms opacity crossfade before exiting the window.
*/
const WINDOW_SIZE = 5;
// Track container width changes (window resize, sidebar toggle, etc.)
$effect(() => {
@@ -249,7 +255,7 @@ $effect(() => {
if (cancelled) {
return;
}
layoutResult = comparisonEngine.layout(
layoutResult = layout.layout(
_text,
fontAStr,
fontBStr,
@@ -328,17 +334,9 @@ $effect(() => {
my-auto
"
>
{#each layoutResult.lines as line}
{@const lineStates = comparisonEngine.getLineCharStates(line, sliderPos, containerWidth)}
<Line chars={line.chars}>
{#snippet character({ char, index })}
<Character
{char}
proximity={lineStates[index]?.proximity ?? 0}
isPast={lineStates[index]?.isPast ?? false}
/>
{/snippet}
</Line>
{#each layoutResult.lines as line, lineIdx (lineIdx)}
{@const model = computeLineRenderModel(line, sliderPos, containerWidth, WINDOW_SIZE)}
<Line {model} />
{/each}
</div>