diff --git a/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte b/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte index bb93baa..3b03a87 100644 --- a/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte +++ b/src/widgets/ComparisonView/ui/SliderArea/SliderArea.svelte @@ -61,6 +61,26 @@ const comparisonEngine = new CharacterComparisonEngine(); let layoutResult = $state>({ lines: [], totalHeight: 0 }); +// Track container width changes (window resize, sidebar toggle, etc.) +$effect(() => { + if (!container) { + return; + } + + const observer = new ResizeObserver(entries => { + for (const entry of entries) { + // Use borderBoxSize if available, fallback to contentRect + const width = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width; + if (width > 0) { + containerWidth = width; + } + } + }); + + observer.observe(container); + return () => observer.disconnect(); +}); + const sliderSpring = new Spring(50, { stiffness: 0.2, damping: 0.7, @@ -124,25 +144,25 @@ $effect(() => { } }); +// Layout effect — depends on content, settings AND containerWidth $effect(() => { const _text = comparisonStore.text; const _weight = typography.weight; const _size = typography.renderedSize; const _height = typography.height; const _spacing = typography.spacing; + const _width = containerWidth; + const _isMobile = isMobile; - if (container && fontA && fontB) { + if (container && fontA && fontB && _width > 0) { // PRETEXT API strings: "weight sizepx family" const fontAStr = getPretextFontString(_weight, _size, fontA.name); const fontBStr = getPretextFontString(_weight, _size, fontB.name); - // Use offsetWidth to avoid transform scaling issues - const width = container.offsetWidth; - const padding = isMobile ? 48 : 96; - const availableWidth = width - padding; + const padding = _isMobile ? 48 : 96; + const availableWidth = Math.max(0, _width - padding); const lineHeight = _size * _height; - containerWidth = width; layoutResult = comparisonEngine.layout( _text, fontAStr, @@ -155,30 +175,6 @@ $effect(() => { } }); -$effect(() => { - if (typeof window === 'undefined') { - return; - } - const handleResize = () => { - if (container && fontA && fontB) { - const width = container.offsetWidth; - const padding = isMobile ? 48 : 96; - containerWidth = width; - layoutResult = comparisonEngine.layout( - comparisonStore.text, - getPretextFontString(typography.weight, typography.renderedSize, fontA.name), - getPretextFontString(typography.weight, typography.renderedSize, fontB.name), - width - padding, - typography.renderedSize * typography.height, - typography.spacing, - typography.renderedSize, - ); - } - }; - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); -}); - // Dynamic backgroundSize based on isMobile — can't express this in Tailwind. // Color is set to currentColor so it respects dark mode via text color. const gridStyle = $derived( @@ -273,8 +269,8 @@ const scaleClass = $derived( class={cn( 'absolute z-10', responsive.isMobileOrTablet - ? 'bottom-4 right-4 -translate-1/2' - : 'bottom-5 left-1/2 right-[unset] -translate-x-1/2', + ? 'bottom-0 right-0 -translate-1/2' + : 'bottom-2.5 left-1/2 -translate-x-1/2', )} />