2026-03-02 22:18:05 +03:00
|
|
|
<!--
|
|
|
|
|
Component: Line
|
|
|
|
|
Renders a line of text in the SliderArea
|
|
|
|
|
-->
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import type { Snippet } from 'svelte';
|
|
|
|
|
import { comparisonStore } from '../../model';
|
|
|
|
|
|
2026-04-11 16:26:41 +03:00
|
|
|
interface LineChar {
|
|
|
|
|
char: string;
|
|
|
|
|
xA: number;
|
|
|
|
|
widthA: number;
|
|
|
|
|
xB: number;
|
|
|
|
|
widthB: number;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 22:18:05 +03:00
|
|
|
interface Props {
|
|
|
|
|
/**
|
2026-04-11 16:26:41 +03:00
|
|
|
* Pre-computed grapheme array from CharacterComparisonEngine.
|
|
|
|
|
* Using the engine's chars array (rather than splitting line.text) ensures
|
|
|
|
|
* correct grapheme-cluster boundaries for emoji and multi-codepoint characters.
|
2026-03-02 22:18:05 +03:00
|
|
|
*/
|
2026-04-11 16:26:41 +03:00
|
|
|
chars: LineChar[];
|
2026-03-02 22:18:05 +03:00
|
|
|
/**
|
|
|
|
|
* Character render snippet
|
|
|
|
|
*/
|
|
|
|
|
character: Snippet<[{ char: string; index: number }]>;
|
|
|
|
|
}
|
|
|
|
|
const typography = $derived(comparisonStore.typography);
|
|
|
|
|
|
2026-04-11 16:26:41 +03:00
|
|
|
let { chars, character }: Props = $props();
|
2026-03-02 22:18:05 +03:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
class="relative flex w-full justify-center items-center whitespace-nowrap"
|
|
|
|
|
style:height="{typography.height}em"
|
|
|
|
|
style:line-height="{typography.height}em"
|
|
|
|
|
>
|
2026-04-11 16:26:41 +03:00
|
|
|
{#each chars as c, index}
|
|
|
|
|
{@render character?.({ char: c.char, index })}
|
2026-03-02 22:18:05 +03:00
|
|
|
{/each}
|
|
|
|
|
</div>
|