refactor(Font): rename fontStore and appliedFontsManager

Both names were vague or overloaded:

- fontStore / FontStore -> fontCatalogStore / FontCatalogStore
  Three font-related stores live in this slice; the new name names the
  paginated catalog specifically.

- appliedFontsManager / AppliedFontsManager -> fontLifecycleManager /
  FontLifecycleManager
  "Applied" collided with the filter-side appliedFilterStore (different
  meaning). The class actually orchestrates a load-use-evict lifecycle
  with FontBufferCache + FontEvictionPolicy + FontLoadQueue
  collaborators, so "Manager" is justified. Companion types file moved
  alongside (appliedFonts.ts -> fontLifecycle.ts).

Directories, file basenames, factory (createFontStore ->
createFontCatalogStore), and the AppliedFontsManagerDeps interface all
renamed. All consumers (ComparisonView, SampleList, FontList,
FontApplicator, FontVirtualList, FilterAndSortFonts bindings,
createFontRowSizeResolver, mocks) updated.
This commit is contained in:
Ilia Mashkov
2026-05-24 20:00:43 +03:00
parent 07d044f4d6
commit 728380498b
38 changed files with 105 additions and 105 deletions
@@ -39,7 +39,7 @@ const fontArialBold = mockUnifiedFont({ id: 'arial-bold', name: 'Arial' });
docs: {
description: {
story:
'Font that has never been loaded by appliedFontsManager. The component renders in its pending state: blurred, scaled down, and semi-transparent.',
'Font that has never been loaded by fontLifecycleManager. The component renders in its pending state: blurred, scaled down, and semi-transparent.',
},
},
}}
@@ -58,7 +58,7 @@ const fontArialBold = mockUnifiedFont({ id: 'arial-bold', name: 'Arial' });
docs: {
description: {
story:
'Uses Arial, a system font available in all browsers. Because appliedFontsManager has not loaded it via FontFace, the manager status may remain pending — meaning the blur/scale state may still show. In a real app the manager would load the font and transition to the revealed state.',
'Uses Arial, a system font available in all browsers. Because fontLifecycleManager has not loaded it via FontFace, the manager status may remain pending — meaning the blur/scale state may still show. In a real app the manager would load the font and transition to the revealed state.',
},
},
}}
@@ -77,7 +77,7 @@ const fontArialBold = mockUnifiedFont({ id: 'arial-bold', name: 'Arial' });
docs: {
description: {
story:
'Demonstrates passing a custom weight (700). The weight is forwarded to appliedFontsManager for font resolution; visually identical to the loaded state story until the manager confirms the font.',
'Demonstrates passing a custom weight (700). The weight is forwarded to fontLifecycleManager for font resolution; visually identical to the loaded state story until the manager confirms the font.',
},
},
}}
@@ -9,7 +9,7 @@ import type { Snippet } from 'svelte';
import {
DEFAULT_FONT_WEIGHT,
type UnifiedFont,
appliedFontsManager,
fontLifecycleManager,
} from '../../model';
interface Props {
@@ -46,7 +46,7 @@ let {
}: Props = $props();
const status = $derived(
appliedFontsManager.getFontStatus(
fontLifecycleManager.getFontStatus(
font.id,
weight,
font.features?.isVariable,
@@ -10,7 +10,7 @@ const { Story } = defineMeta({
docs: {
description: {
component:
'Virtualized font list backed by the `fontStore` singleton. Handles font loading registration (pin/touch) for visible items and triggers infinite scroll pagination via `fontStore.nextPage()`. Because the component reads directly from the `fontStore` singleton, stories render against a live (but empty/loading) store — no font data will appear unless the API is reachable from the Storybook host.',
'Virtualized font list backed by the `fontCatalogStore` singleton. Handles font loading registration (pin/touch) for visible items and triggers infinite scroll pagination via `fontCatalogStore.nextPage()`. Because the component reads directly from the `fontCatalogStore` singleton, stories render against a live (but empty/loading) store — no font data will appear unless the API is reachable from the Storybook host.',
},
story: { inline: false },
},
@@ -33,7 +33,7 @@ import type { ComponentProps } from 'svelte';
docs: {
description: {
story:
'Skeleton state shown while `fontStore.fonts` is empty and `fontStore.isLoading` is true. In a real session the skeleton fades out once the first page loads.',
'Skeleton state shown while `fontCatalogStore.fonts` is empty and `fontCatalogStore.isLoading` is true. In a real session the skeleton fades out once the first page loads.',
},
},
}}
@@ -63,7 +63,7 @@ import type { ComponentProps } from 'svelte';
docs: {
description: {
story:
'No `skeleton` snippet provided. When `fontStore.fonts` is empty the underlying VirtualList renders its empty state directly.',
'No `skeleton` snippet provided. When `fontCatalogStore.fonts` is empty the underlying VirtualList renders its empty state directly.',
},
},
}}
@@ -86,7 +86,7 @@ import type { ComponentProps } from 'svelte';
docs: {
description: {
story:
'Demonstrates how to configure a `children` snippet for item rendering. The list will be empty because `fontStore` is not populated in Storybook, but the template shows the expected slot shape: `{ item: UnifiedFont }`.',
'Demonstrates how to configure a `children` snippet for item rendering. The list will be empty because `fontCatalogStore` is not populated in Storybook, but the template shows the expected slot shape: `{ item: UnifiedFont }`.',
},
},
}}
@@ -18,8 +18,8 @@ import { getFontUrl } from '../../lib';
import {
type FontLoadRequestConfig,
type UnifiedFont,
appliedFontsManager,
fontStore,
fontCatalogStore,
fontLifecycleManager,
} from '../../model';
interface Props extends
@@ -51,13 +51,13 @@ let {
}: Props = $props();
const isLoading = $derived(
fontStore.isFetching || fontStore.isLoading,
fontCatalogStore.isFetching || fontCatalogStore.isLoading,
);
let visibleFonts = $state<UnifiedFont[]>([]);
let isCatchingUp = $state(false);
const showInitialSkeleton = $derived(!!skeleton && isLoading && fontStore.fonts.length === 0);
const showInitialSkeleton = $derived(!!skeleton && isLoading && fontCatalogStore.fonts.length === 0);
const showCatchupSkeleton = $derived(!!skeleton && isCatchingUp);
function handleInternalVisibleChange(items: UnifiedFont[]) {
@@ -68,23 +68,23 @@ function handleInternalVisibleChange(items: UnifiedFont[]) {
/**
* Handle jump scroll — batch-load all missing pages then re-enable font loading.
* Suppresses appliedFontsManager.touch() during catch-up to avoid loading
* Suppresses fontLifecycleManager.touch() during catch-up to avoid loading
* font files for thousands of intermediate fonts.
*/
async function handleJump(targetIndex: number) {
if (isCatchingUp || !fontStore.pagination.hasMore) {
if (isCatchingUp || !fontCatalogStore.pagination.hasMore) {
return;
}
isCatchingUp = true;
try {
await fontStore.fetchAllPagesTo(targetIndex);
await fontCatalogStore.fetchAllPagesTo(targetIndex);
} finally {
isCatchingUp = false;
}
}
const debouncedTouch = debounce((configs: FontLoadRequestConfig[]) => {
appliedFontsManager.touch(configs);
fontLifecycleManager.touch(configs);
}, 150);
// Re-touch whenever visible set or weight changes — fixes weight-change gap
@@ -111,11 +111,11 @@ $effect(() => {
const w = weight;
const fonts = visibleFonts;
for (const f of fonts) {
appliedFontsManager.pin(f.id, w, f.features?.isVariable);
fontLifecycleManager.pin(f.id, w, f.features?.isVariable);
}
return () => {
for (const f of fonts) {
appliedFontsManager.unpin(f.id, w, f.features?.isVariable);
fontLifecycleManager.unpin(f.id, w, f.features?.isVariable);
}
};
});
@@ -125,12 +125,12 @@ $effect(() => {
*/
function loadMore() {
if (
!fontStore.pagination.hasMore
|| fontStore.isFetching
!fontCatalogStore.pagination.hasMore
|| fontCatalogStore.isFetching
) {
return;
}
fontStore.nextPage();
fontCatalogStore.nextPage();
}
/**
@@ -140,12 +140,12 @@ function loadMore() {
* of the loaded items. Only fetches if there are more pages available.
*/
function handleNearBottom(_lastVisibleIndex: number) {
const { hasMore } = fontStore.pagination;
const { hasMore } = fontCatalogStore.pagination;
// VirtualList already checks if we're near the bottom of loaded items.
// Guard isCatchingUp: fetchAllPagesTo bypasses TQ so isFetching stays false
// during batch catch-up, which would otherwise let nextPage() race with it.
if (hasMore && !fontStore.isFetching && !isCatchingUp) {
if (hasMore && !fontCatalogStore.isFetching && !isCatchingUp) {
loadMore();
}
}
@@ -160,8 +160,8 @@ function handleNearBottom(_lastVisibleIndex: number) {
{:else}
<!-- VirtualList persists during pagination - no destruction/recreation -->
<VirtualList
items={fontStore.fonts}
total={fontStore.pagination.total}
items={fontCatalogStore.fonts}
total={fontCatalogStore.pagination.total}
isLoading={isLoading || isCatchingUp}
onVisibleItemsChange={handleInternalVisibleChange}
onNearBottom={handleNearBottom}