From 63888e510cabb161a80ac28d258dc3892867a460 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 11:43:39 +0300 Subject: [PATCH 01/12] feat(Spinner): add shadcn spinner component --- src/shared/shadcn/ui/spinner/spinner.svelte | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/shared/shadcn/ui/spinner/spinner.svelte diff --git a/src/shared/shadcn/ui/spinner/spinner.svelte b/src/shared/shadcn/ui/spinner/spinner.svelte new file mode 100644 index 0000000..e897fae --- /dev/null +++ b/src/shared/shadcn/ui/spinner/spinner.svelte @@ -0,0 +1,14 @@ + + + -- 2.49.1 From b9eccbf627e2cc206117b9e1609330219cc0543f Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 11:53:59 +0300 Subject: [PATCH 02/12] feat(Skeleton): create skeleton component and integrate it into FontVirtualList --- .../ui/FontVirtualList/FontVirtualList.svelte | 77 +++++++++++++++---- .../ui/Skeleton/Skeleton.stories.svelte | 41 ++++++++++ src/shared/ui/Skeleton/Skeleton.svelte | 27 +++++++ 3 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 src/shared/ui/Skeleton/Skeleton.stories.svelte create mode 100644 src/shared/ui/Skeleton/Skeleton.svelte diff --git a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte index ec899d9..984b89a 100644 --- a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte +++ b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte @@ -4,8 +4,12 @@ - Handles font registration with the manager --> - - {#snippet children(scope)} - {@render children(scope)} - {/snippet} - +{#key isLoading} +
+ {#if isLoading} +
+ {#each Array(5) as _, i} +
+
+ + +
+ +
+ {/each} +
+ {:else} + + {#snippet children(scope)} + {@render children(scope)} + {/snippet} + + {/if} +
+{/key} diff --git a/src/shared/ui/Skeleton/Skeleton.stories.svelte b/src/shared/ui/Skeleton/Skeleton.stories.svelte new file mode 100644 index 0000000..426efb8 --- /dev/null +++ b/src/shared/ui/Skeleton/Skeleton.stories.svelte @@ -0,0 +1,41 @@ + + + +
+
+
+ + +
+ +
+
+
diff --git a/src/shared/ui/Skeleton/Skeleton.svelte b/src/shared/ui/Skeleton/Skeleton.svelte new file mode 100644 index 0000000..0e28399 --- /dev/null +++ b/src/shared/ui/Skeleton/Skeleton.svelte @@ -0,0 +1,27 @@ + + + +
+
-- 2.49.1 From 195ae09fa22d7739e006c234884764e54c95467b Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 11:54:09 +0300 Subject: [PATCH 03/12] feat(Spinner): add shadcn spinner component --- src/shared/shadcn/ui/spinner/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/shared/shadcn/ui/spinner/index.ts diff --git a/src/shared/shadcn/ui/spinner/index.ts b/src/shared/shadcn/ui/spinner/index.ts new file mode 100644 index 0000000..2e459c6 --- /dev/null +++ b/src/shared/shadcn/ui/spinner/index.ts @@ -0,0 +1 @@ +export { default as Spinner } from './spinner.svelte'; -- 2.49.1 From 51c2b6b5da261ba3b93cf46930ffababc11a0066 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 11:54:36 +0300 Subject: [PATCH 04/12] chore(Page): change icon --- src/routes/Page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Page.svelte b/src/routes/Page.svelte index 6563b4f..87dde72 100644 --- a/src/routes/Page.svelte +++ b/src/routes/Page.svelte @@ -10,8 +10,8 @@ import ComparisonSlider from '$widgets/ComparisonSlider/ui/ComparisonSlider/Comp import { FontSearch } from '$widgets/FontSearch'; import { SampleList } from '$widgets/SampleList'; import CodeIcon from '@lucide/svelte/icons/code'; +import EyeIcon from '@lucide/svelte/icons/eye'; import LineSquiggleIcon from '@lucide/svelte/icons/line-squiggle'; -import ScanEyeIcon from '@lucide/svelte/icons/scan-eye'; import ScanSearchIcon from '@lucide/svelte/icons/search'; import type { Snippet } from 'svelte'; @@ -59,7 +59,7 @@ function handleTitleStatusChanged(index: number, isPast: boolean, title?: Snippe
{#snippet icon({ className })} - + {/snippet} {#snippet title({ className })}

-- 2.49.1 From e4aacf609e563ac60682c642faf901eb974ad79c Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 11:55:05 +0300 Subject: [PATCH 05/12] feat(VirtualList): add isLoading prop --- src/shared/ui/VirtualList/VirtualList.svelte | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/shared/ui/VirtualList/VirtualList.svelte b/src/shared/ui/VirtualList/VirtualList.svelte index d4e2f05..b0de643 100644 --- a/src/shared/ui/VirtualList/VirtualList.svelte +++ b/src/shared/ui/VirtualList/VirtualList.svelte @@ -101,13 +101,25 @@ interface Props { * @template T - The type of items in the list */ children: Snippet< - [{ item: T; index: number; isFullyVisible: boolean; isPartiallyVisible: boolean; proximity: number }] + [ + { + item: T; + index: number; + isFullyVisible: boolean; + isPartiallyVisible: boolean; + proximity: number; + }, + ] >; /** * Whether to use the window as the scroll container. * @default false */ useWindowScroll?: boolean; + /** + * Flag to show loading state + */ + isLoading?: boolean; } let { @@ -120,6 +132,7 @@ let { onNearBottom, children, useWindowScroll = false, + isLoading = false, }: Props = $props(); // Reference to the ScrollArea viewport element for attaching the virtualizer -- 2.49.1 From 4b440496babfcc5489c7ef15184813f3613d03de Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 11:55:31 +0300 Subject: [PATCH 06/12] feat(comparisonStore): add isLoading getter --- .../ComparisonSlider/model/stores/comparisonStore.svelte.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts b/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts index 503900f..f6ae670 100644 --- a/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts +++ b/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts @@ -136,6 +136,10 @@ class ComparisonStore { return !!this.#fontA && !!this.#fontB; } + get isLoading() { + return this.#isRestoring; + } + /** * Public initializer (optional, as constructor starts it) * Kept for compatibility if manual re-init is needed -- 2.49.1 From 3ed63562b7e68fc6438ca236e1455bf420fd0147 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 12:03:06 +0300 Subject: [PATCH 07/12] feat(Loader): create loader component with spinner and optional message --- src/shared/ui/Loader/Loader.stories.svelte | 33 ++++++++++ src/shared/ui/Loader/Loader.svelte | 77 ++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/shared/ui/Loader/Loader.stories.svelte create mode 100644 src/shared/ui/Loader/Loader.svelte diff --git a/src/shared/ui/Loader/Loader.stories.svelte b/src/shared/ui/Loader/Loader.stories.svelte new file mode 100644 index 0000000..37739e3 --- /dev/null +++ b/src/shared/ui/Loader/Loader.stories.svelte @@ -0,0 +1,33 @@ + + + + + diff --git a/src/shared/ui/Loader/Loader.svelte b/src/shared/ui/Loader/Loader.svelte new file mode 100644 index 0000000..2789f74 --- /dev/null +++ b/src/shared/ui/Loader/Loader.svelte @@ -0,0 +1,77 @@ + + + +
+
+ + + + + + + + + + + + + +
+ +
+ + + + {message} + +
-- 2.49.1 From b304e841de5ee97834006b334818e1cb68dd7d10 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 12:04:32 +0300 Subject: [PATCH 08/12] feat(ComparisonSlider): integrate loader and add animations for appearance/disappearance --- .../ComparisonSlider/ComparisonSlider.svelte | 64 +++++++++++-------- .../components/ControlsWrapper.svelte | 3 + .../ComparisonSlider/components/Labels.svelte | 3 + 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte b/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte index 2120233..bcd3eef 100644 --- a/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte +++ b/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte @@ -15,8 +15,10 @@ import { createTypographyControl, } from '$shared/lib'; import type { LineData } from '$shared/lib'; +import { Loader } from '$shared/ui'; import { comparisonStore } from '$widgets/ComparisonSlider/model'; import { Spring } from 'svelte/motion'; +import { fade } from 'svelte/transition'; import CharacterSlot from './components/CharacterSlot.svelte'; import ControlsWrapper from './components/ControlsWrapper.svelte'; import Labels from './components/Labels.svelte'; @@ -26,6 +28,8 @@ import SliderLine from './components/SliderLine.svelte'; const fontA = $derived(comparisonStore.fontA); const fontB = $derived(comparisonStore.fontB); +const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady); + let container: HTMLElement | undefined = $state(); let controlsWrapperElement = $state(null); let measureCanvas: HTMLCanvasElement | undefined = $state(); @@ -164,31 +168,33 @@ $effect(() => { {/snippet} -{#if fontA && fontB} - - + + -
-
- +
+
+ + {#if isLoading} + + {:else}
{#each charComparison.lines as line, lineIndex}
{
-
+ {/if} +
+ {#if fontA && fontB && !isLoading} { {sizeControl} {heightControl} /> -
-{/if} + {/if} +
diff --git a/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte b/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte index c23106b..7dd0fe7 100644 --- a/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte +++ b/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/ControlsWrapper.svelte @@ -12,6 +12,7 @@ import { ComboControlV2 } from '$shared/ui'; import { ExpandableWrapper } from '$shared/ui'; import AArrowUP from '@lucide/svelte/icons/a-arrow-up'; import { Spring } from 'svelte/motion'; +import { fade } from 'svelte/transition'; interface Props { /** @@ -121,6 +122,8 @@ $effect(() => { translateX({xSpring.current}px) rotateZ({rotateSpring.current}deg) " + in:fade={{ duration: 300, delay: 300 }} + out:fade={{ duration: 300, delay: 300 }} > { /** @@ -60,6 +61,8 @@ function selectFontB(font: UnifiedFont) {
e.stopPropagation())} + in:fade={{ duration: 300, delay: 300 }} + out:fade={{ duration: 300, delay: 300 }} > Date: Fri, 6 Feb 2026 12:04:53 +0300 Subject: [PATCH 09/12] chore: change export/import --- src/shared/ui/index.ts | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts index a8429cb..c5161a2 100644 --- a/src/shared/ui/index.ts +++ b/src/shared/ui/index.ts @@ -1,27 +1,12 @@ -/** - * Shared UI components exports - * - * Exports all shared UI components and their types - */ - -import CheckboxFilter from './CheckboxFilter/CheckboxFilter.svelte'; -import ComboControl from './ComboControl/ComboControl.svelte'; -import ComboControlV2 from './ComboControlV2/ComboControlV2.svelte'; -import ContentEditable from './ContentEditable/ContentEditable.svelte'; -import ExpandableWrapper from './ExpandableWrapper/ExpandableWrapper.svelte'; -import IconButton from './IconButton/IconButton.svelte'; -import SearchBar from './SearchBar/SearchBar.svelte'; -import Section from './Section/Section.svelte'; -import VirtualList from './VirtualList/VirtualList.svelte'; - -export { - CheckboxFilter, - ComboControl, - ComboControlV2, - ContentEditable, - ExpandableWrapper, - IconButton, - SearchBar, - Section, - VirtualList, -}; +export { default as CheckboxFilter } from './CheckboxFilter/CheckboxFilter.svelte'; +export { default as ComboControl } from './ComboControl/ComboControl.svelte'; +// ComboControlV2 might vary, assuming pattern holds or I'll fix later if build fails +export { default as ComboControlV2 } from './ComboControlV2/ComboControlV2.svelte'; +export { default as ContentEditable } from './ContentEditable/ContentEditable.svelte'; +export { default as ExpandableWrapper } from './ExpandableWrapper/ExpandableWrapper.svelte'; +export { default as IconButton } from './IconButton/IconButton.svelte'; +export { default as Loader } from './Loader/Loader.svelte'; +export { default as SearchBar } from './SearchBar/SearchBar.svelte'; +export { default as Section } from './Section/Section.svelte'; +export { default as Skeleton } from './Skeleton/Skeleton.svelte'; +export { default as VirtualList } from './VirtualList/VirtualList.svelte'; -- 2.49.1 From 88f4cd97f900483fe51ef355ce99916a55514d37 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 12:05:29 +0300 Subject: [PATCH 10/12] feat(SampleList): remove text loader and add a prop isLoading --- .../SampleList/ui/SampleList/SampleList.svelte | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/widgets/SampleList/ui/SampleList/SampleList.svelte b/src/widgets/SampleList/ui/SampleList/SampleList.svelte index 112f3fb..9ffad72 100644 --- a/src/widgets/SampleList/ui/SampleList/SampleList.svelte +++ b/src/widgets/SampleList/ui/SampleList/SampleList.svelte @@ -50,11 +50,9 @@ const displayRange = $derived.by(() => { const loadedCount = Math.min(offset + limit, total); return `Showing ${loadedCount} of ${total} fonts`; }); - -{#if unifiedFontStore.isFetching || unifiedFontStore.isLoading} - (Loading...) -{/if} +const isLoading = $derived(unifiedFontStore.isFetching || unifiedFontStore.isLoading); + { itemHeight={280} useWindowScroll={true} weight={controlManager.weight} + {isLoading} > - {#snippet children({ item: font, isFullyVisible, isPartiallyVisible, proximity, index })} + {#snippet children({ + item: font, + isFullyVisible, + isPartiallyVisible, + proximity, + index, +})} -- 2.49.1 From 3537f6f62c0fd38caa0b1232adc387d4ed28f0e3 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 12:16:42 +0300 Subject: [PATCH 11/12] fix(FontSearch): change button size to normal --- src/widgets/FontSearch/ui/FontSearch/FontSearch.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/FontSearch/ui/FontSearch/FontSearch.svelte b/src/widgets/FontSearch/ui/FontSearch/FontSearch.svelte index e8596c7..73ee63c 100644 --- a/src/widgets/FontSearch/ui/FontSearch/FontSearch.svelte +++ b/src/widgets/FontSearch/ui/FontSearch/FontSearch.svelte @@ -120,7 +120,7 @@ function toggleFilters() {
- +
-- 2.49.1 From 1b9fe14f01e2ce3d4721755265a0695a1223e93e Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Fri, 6 Feb 2026 12:17:11 +0300 Subject: [PATCH 12/12] fix(FontSampler): comment unused button --- .../ui/FontSampler/FontSampler.svelte | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte b/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte index 453c23f..9d74d11 100644 --- a/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte +++ b/src/features/DisplayFont/ui/FontSampler/FontSampler.svelte @@ -11,9 +11,9 @@ import { import { controlManager } from '$features/SetupFont'; import { ContentEditable, - IconButton, + // IconButton, } from '$shared/ui'; -import XIcon from '@lucide/svelte/icons/x'; +// import XIcon from '@lucide/svelte/icons/x'; interface Props { /** @@ -75,14 +75,16 @@ function removeSample() { - - {#snippet icon({ className })} - - {/snippet} - +
-- 2.49.1