From 728380498ba67935dca5b2fa050aca5309d230e3 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 24 May 2026 20:00:43 +0300 Subject: [PATCH] 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. --- src/entities/Font/lib/mocks/stores.mock.ts | 6 ++-- .../sizeResolver/createFontRowSizeResolver.ts | 6 ++-- .../fontCatalogStore.svelte.spec.ts} | 18 +++++----- .../fontCatalogStore.svelte.ts} | 8 ++--- .../errors.ts | 0 .../fontLifecycleManager.svelte.ts} | 8 ++--- .../fontLifecycleManager.test.ts} | 16 ++++----- .../fontBufferCache/FontBufferCache.test.ts | 0 .../utils/fontBufferCache/FontBufferCache.ts | 0 .../FontEvictionPolicy.test.ts | 0 .../fontEvictionPolicy/FontEvictionPolicy.ts | 0 .../utils/fontLoadQueue/FontLoadQueue.test.ts | 0 .../utils/fontLoadQueue/FontLoadQueue.ts | 0 .../generateFontKey/generateFontKey.test.ts | 0 .../utils/generateFontKey/generateFontKey.ts | 0 .../getEffectiveConcurrency.test.ts | 0 .../getEffectiveConcurrency.ts | 0 .../utils/index.ts | 0 .../utils/loadFont/loadFont.test.ts | 0 .../utils/loadFont/loadFont.ts | 0 .../yieldToMainThread.test.ts | 0 .../yieldToMainThread/yieldToMainThread.ts | 0 src/entities/Font/model/store/index.ts | 14 ++++---- src/entities/Font/model/types/index.ts | 2 +- .../{appliedFonts.ts => fontLifecycle.ts} | 0 .../FontApplicator.stories.svelte | 6 ++-- .../ui/FontApplicator/FontApplicator.svelte | 4 +-- .../FontVirtualList.stories.svelte | 8 ++--- .../ui/FontVirtualList/FontVirtualList.svelte | 34 +++++++++---------- .../FilterAndSortFonts/model/index.ts | 2 +- .../model/store/bindings.svelte.ts | 14 ++++---- .../createVirtualizer.svelte.ts | 2 +- .../model/stores/comparisonStore.svelte.ts | 16 ++++----- .../model/stores/comparisonStore.test.ts | 20 +++++------ .../ui/FontList/FontList.svelte | 10 +++--- .../ComparisonView/ui/Search/Search.svelte | 2 +- .../ui/SampleList/SampleList.svelte | 10 +++--- .../SampleListSection.svelte | 4 +-- 38 files changed, 105 insertions(+), 105 deletions(-) rename src/entities/Font/model/store/{fontStore/fontStore.svelte.spec.ts => fontCatalogStore/fontCatalogStore.svelte.spec.ts} (98%) rename src/entities/Font/model/store/{fontStore/fontStore.svelte.ts => fontCatalogStore/fontCatalogStore.svelte.ts} (98%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/errors.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore/appliedFontsStore.svelte.ts => fontLifecycleManager/fontLifecycleManager.svelte.ts} (98%) rename src/entities/Font/model/store/{appliedFontsStore/appliedFontStore.test.ts => fontLifecycleManager/fontLifecycleManager.test.ts} (94%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/fontBufferCache/FontBufferCache.test.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/fontBufferCache/FontBufferCache.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/fontEvictionPolicy/FontEvictionPolicy.test.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/fontEvictionPolicy/FontEvictionPolicy.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/fontLoadQueue/FontLoadQueue.test.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/fontLoadQueue/FontLoadQueue.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/generateFontKey/generateFontKey.test.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/generateFontKey/generateFontKey.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/getEffectiveConcurrency/getEffectiveConcurrency.test.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/getEffectiveConcurrency/getEffectiveConcurrency.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/index.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/loadFont/loadFont.test.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/loadFont/loadFont.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/yieldToMainThread/yieldToMainThread.test.ts (100%) rename src/entities/Font/model/store/{appliedFontsStore => fontLifecycleManager}/utils/yieldToMainThread/yieldToMainThread.ts (100%) rename src/entities/Font/model/types/store/{appliedFonts.ts => fontLifecycle.ts} (100%) diff --git a/src/entities/Font/lib/mocks/stores.mock.ts b/src/entities/Font/lib/mocks/stores.mock.ts index fd54060..030b581 100644 --- a/src/entities/Font/lib/mocks/stores.mock.ts +++ b/src/entities/Font/lib/mocks/stores.mock.ts @@ -667,10 +667,10 @@ export const MOCK_STORES = { }; }, /** - * Create a mock FontStore object - * Matches FontStore's public API for Storybook use + * Create a mock FontCatalogStore object + * Matches FontCatalogStore's public API for Storybook use */ - fontStore: (config: { + fontCatalogStore: (config: { /** * Preset font list */ diff --git a/src/entities/Font/lib/sizeResolver/createFontRowSizeResolver.ts b/src/entities/Font/lib/sizeResolver/createFontRowSizeResolver.ts index 29afcf5..93e76fa 100644 --- a/src/entities/Font/lib/sizeResolver/createFontRowSizeResolver.ts +++ b/src/entities/Font/lib/sizeResolver/createFontRowSizeResolver.ts @@ -1,5 +1,5 @@ import { TextLayoutEngine } from '$shared/lib'; -import { generateFontKey } from '../../model/store/appliedFontsStore/utils/generateFontKey/generateFontKey'; +import { generateFontKey } from '../../model/store/fontLifecycleManager/utils/generateFontKey/generateFontKey'; import type { FontLoadStatus, UnifiedFont, @@ -41,7 +41,7 @@ export interface FontRowSizeResolverOptions { /** * Returns the font load status for a given font key (`'{id}@{weight}'` or `'{id}@vf'`). * - * In production: `(key) => appliedFontsManager.statuses.get(key)`. + * In production: `(key) => fontLifecycleManager.statuses.get(key)`. * Injected for testability — avoids a module-level singleton dependency in tests. * The call to `.get()` on a `SvelteMap` must happen inside a `$derived.by` context * for reactivity to work. This is satisfied when `itemHeight` is called by @@ -108,7 +108,7 @@ export function createFontRowSizeResolver(options: FontRowSizeResolverOptions): // generateFontKey: '{id}@{weight}' for static fonts, '{id}@vf' for variable fonts. const fontKey = generateFontKey({ id: font.id, weight, isVariable: font.features?.isVariable }); - // Reading via getStatus() allows the caller to pass appliedFontsManager.statuses.get(), + // Reading via getStatus() allows the caller to pass fontLifecycleManager.statuses.get(), // which creates a Svelte 5 reactive dependency when called inside $derived.by. const status = options.getStatus(fontKey); if (status !== 'loaded') { diff --git a/src/entities/Font/model/store/fontStore/fontStore.svelte.spec.ts b/src/entities/Font/model/store/fontCatalogStore/fontCatalogStore.svelte.spec.ts similarity index 98% rename from src/entities/Font/model/store/fontStore/fontStore.svelte.spec.ts rename to src/entities/Font/model/store/fontCatalogStore/fontCatalogStore.svelte.spec.ts index d2e058c..5cdae3a 100644 --- a/src/entities/Font/model/store/fontStore/fontStore.svelte.spec.ts +++ b/src/entities/Font/model/store/fontCatalogStore/fontCatalogStore.svelte.spec.ts @@ -17,7 +17,7 @@ import { generateMockFonts, } from '../../../lib/mocks/fonts.mock'; import type { UnifiedFont } from '../../types'; -import { FontStore } from './fontStore.svelte'; +import { FontCatalogStore } from './fontCatalogStore.svelte'; vi.mock('$shared/api/queryClient', () => ({ queryClient: new QueryClient({ @@ -44,7 +44,7 @@ const makeResponse = ( }); function makeStore(params = {}) { - return new FontStore({ limit: 10, ...params }); + return new FontCatalogStore({ limit: 10, ...params }); } async function fetchedStore(params = {}, fonts = generateMockFonts(5), meta: Parameters[1] = {}) { @@ -55,7 +55,7 @@ async function fetchedStore(params = {}, fonts = generateMockFonts(5), meta: Par return store; } -describe('FontStore', () => { +describe('FontCatalogStore', () => { afterEach(() => { queryClient.clear(); vi.resetAllMocks(); @@ -69,7 +69,7 @@ describe('FontStore', () => { }); it('defaults limit to 50 when not provided', () => { - const store = new FontStore(); + const store = new FontCatalogStore(); expect(store.params.limit).toBe(50); store.destroy(); }); @@ -390,11 +390,11 @@ describe('FontStore', () => { }); describe('nextPage', () => { - let store: FontStore; + let store: FontCatalogStore; beforeEach(async () => { fetch.mockResolvedValue(makeResponse(generateMockFonts(10), { total: 30, limit: 10, offset: 0 })); - store = new FontStore({ limit: 10 }); + store = new FontCatalogStore({ limit: 10 }); await store.refetch(); flushSync(); }); @@ -415,7 +415,7 @@ describe('FontStore', () => { // Set up a store where all fonts fit in one page (hasMore = false) queryClient.clear(); fetch.mockResolvedValue(makeResponse(generateMockFonts(10), { total: 10, limit: 10, offset: 0 })); - store = new FontStore({ limit: 10 }); + store = new FontCatalogStore({ limit: 10 }); await store.refetch(); flushSync(); @@ -454,7 +454,7 @@ describe('FontStore', () => { describe('getCachedData / setQueryData', () => { it('getCachedData returns undefined before any fetch', () => { queryClient.clear(); - const store = new FontStore({ limit: 10 }); + const store = new FontCatalogStore({ limit: 10 }); expect(store.getCachedData()).toBeUndefined(); store.destroy(); }); @@ -502,7 +502,7 @@ describe('FontStore', () => { }); describe('filter shortcut methods', () => { - let store: FontStore; + let store: FontCatalogStore; beforeEach(() => { store = makeStore(); diff --git a/src/entities/Font/model/store/fontStore/fontStore.svelte.ts b/src/entities/Font/model/store/fontCatalogStore/fontCatalogStore.svelte.ts similarity index 98% rename from src/entities/Font/model/store/fontStore/fontStore.svelte.ts rename to src/entities/Font/model/store/fontCatalogStore/fontCatalogStore.svelte.ts index aeeb9cc..165a5ab 100644 --- a/src/entities/Font/model/store/fontStore/fontStore.svelte.ts +++ b/src/entities/Font/model/store/fontCatalogStore/fontCatalogStore.svelte.ts @@ -25,7 +25,7 @@ type FontStoreParams = Omit; type FontStoreResult = InfiniteQueryObserverResult, Error>; -export class FontStore { +export class FontCatalogStore { #params = $state({ limit: 50 }); #result = $state({} as FontStoreResult); #observer: InfiniteQueryObserver< @@ -459,8 +459,8 @@ export class FontStore { } } -export function createFontStore(params: FontStoreParams = {}): FontStore { - return new FontStore(params); +export function createFontCatalogStore(params: FontStoreParams = {}): FontCatalogStore { + return new FontCatalogStore(params); } -export const fontStore = new FontStore({ limit: 50 }); +export const fontCatalogStore = new FontCatalogStore({ limit: 50 }); diff --git a/src/entities/Font/model/store/appliedFontsStore/errors.ts b/src/entities/Font/model/store/fontLifecycleManager/errors.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/errors.ts rename to src/entities/Font/model/store/fontLifecycleManager/errors.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/appliedFontsStore.svelte.ts b/src/entities/Font/model/store/fontLifecycleManager/fontLifecycleManager.svelte.ts similarity index 98% rename from src/entities/Font/model/store/appliedFontsStore/appliedFontsStore.svelte.ts rename to src/entities/Font/model/store/fontLifecycleManager/fontLifecycleManager.svelte.ts index 48bf55c..1a4755e 100644 --- a/src/entities/Font/model/store/appliedFontsStore/appliedFontsStore.svelte.ts +++ b/src/entities/Font/model/store/fontLifecycleManager/fontLifecycleManager.svelte.ts @@ -17,7 +17,7 @@ import { FontBufferCache } from './utils/fontBufferCache/FontBufferCache'; import { FontEvictionPolicy } from './utils/fontEvictionPolicy/FontEvictionPolicy'; import { FontLoadQueue } from './utils/fontLoadQueue/FontLoadQueue'; -interface AppliedFontsManagerDeps { +interface FontLifecycleManagerDeps { cache?: FontBufferCache; eviction?: FontEvictionPolicy; queue?: FontLoadQueue; @@ -46,7 +46,7 @@ interface AppliedFontsManagerDeps { * * **Browser APIs Used:** `scheduler.yield()`, `isInputPending()`, `requestIdleCallback`, Cache API, Network Information API */ -export class AppliedFontsManager { +export class FontLifecycleManager { // Injected collaborators - each handles one concern for better testability readonly #cache: FontBufferCache; readonly #eviction: FontEvictionPolicy; @@ -78,7 +78,7 @@ export class AppliedFontsManager { // Starts periodic cleanup timer (browser-only). constructor( { cache = new FontBufferCache(), eviction = new FontEvictionPolicy(), queue = new FontLoadQueue() }: - AppliedFontsManagerDeps = {}, + FontLifecycleManagerDeps = {}, ) { // Inject collaborators - defaults provided for production, fakes for testing this.#cache = cache; @@ -396,4 +396,4 @@ export class AppliedFontsManager { /** * Singleton instance — use throughout the application for unified font loading state. */ -export const appliedFontsManager = new AppliedFontsManager(); +export const fontLifecycleManager = new FontLifecycleManager(); diff --git a/src/entities/Font/model/store/appliedFontsStore/appliedFontStore.test.ts b/src/entities/Font/model/store/fontLifecycleManager/fontLifecycleManager.test.ts similarity index 94% rename from src/entities/Font/model/store/appliedFontsStore/appliedFontStore.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/fontLifecycleManager.test.ts index b1183ba..4c7e4ed 100644 --- a/src/entities/Font/model/store/appliedFontsStore/appliedFontStore.test.ts +++ b/src/entities/Font/model/store/fontLifecycleManager/fontLifecycleManager.test.ts @@ -1,8 +1,8 @@ /** * @vitest-environment jsdom */ -import { AppliedFontsManager } from './appliedFontsStore.svelte'; import { FontFetchError } from './errors'; +import { FontLifecycleManager } from './fontLifecycleManager.svelte'; import { FontEvictionPolicy } from './utils/fontEvictionPolicy/FontEvictionPolicy'; class FakeBufferCache { @@ -32,8 +32,8 @@ const makeConfig = (id: string, overrides: Partial<{ weight: number; isVariable: ...overrides, }); -describe('AppliedFontsManager', () => { - let manager: AppliedFontsManager; +describe('FontLifecycleManager', () => { + let manager: FontLifecycleManager; let eviction: FontEvictionPolicy; let mockFontFaceSet: { add: ReturnType; delete: ReturnType }; @@ -55,7 +55,7 @@ describe('AppliedFontsManager', () => { }); vi.stubGlobal('FontFace', MockFontFace); - manager = new AppliedFontsManager({ cache: new FakeBufferCache() as any, eviction }); + manager = new FontLifecycleManager({ cache: new FakeBufferCache() as any, eviction }); }); afterEach(() => { @@ -101,7 +101,7 @@ describe('AppliedFontsManager', () => { it('skips fonts that have exhausted retries', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); - const failManager = new AppliedFontsManager({ cache: new FailingBufferCache() as any, eviction }); + const failManager = new FontLifecycleManager({ cache: new FailingBufferCache() as any, eviction }); // exhaust all 3 retries for (let i = 0; i < 3; i++) { @@ -160,7 +160,7 @@ describe('AppliedFontsManager', () => { describe('Phase 1 — fetch', () => { it('sets status to error on fetch failure', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); - const failManager = new AppliedFontsManager({ cache: new FailingBufferCache() as any, eviction }); + const failManager = new FontLifecycleManager({ cache: new FailingBufferCache() as any, eviction }); failManager.touch([makeConfig('broken')]); await vi.advanceTimersByTimeAsync(50); @@ -171,7 +171,7 @@ describe('AppliedFontsManager', () => { it('logs a console error on fetch failure', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); - const failManager = new AppliedFontsManager({ cache: new FailingBufferCache() as any, eviction }); + const failManager = new FontLifecycleManager({ cache: new FailingBufferCache() as any, eviction }); failManager.touch([makeConfig('broken')]); await vi.advanceTimersByTimeAsync(50); @@ -189,7 +189,7 @@ describe('AppliedFontsManager', () => { evict() {}, clear() {}, }; - const abortManager = new AppliedFontsManager({ cache: abortingCache as any, eviction }); + const abortManager = new FontLifecycleManager({ cache: abortingCache as any, eviction }); abortManager.touch([makeConfig('aborted')]); await vi.advanceTimersByTimeAsync(50); diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/fontBufferCache/FontBufferCache.test.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/fontBufferCache/FontBufferCache.test.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/fontBufferCache/FontBufferCache.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/fontBufferCache/FontBufferCache.test.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/fontBufferCache/FontBufferCache.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/fontBufferCache/FontBufferCache.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/fontBufferCache/FontBufferCache.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/fontBufferCache/FontBufferCache.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/fontEvictionPolicy/FontEvictionPolicy.test.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/fontEvictionPolicy/FontEvictionPolicy.test.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/fontEvictionPolicy/FontEvictionPolicy.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/fontEvictionPolicy/FontEvictionPolicy.test.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/fontEvictionPolicy/FontEvictionPolicy.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/fontEvictionPolicy/FontEvictionPolicy.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/fontEvictionPolicy/FontEvictionPolicy.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/fontEvictionPolicy/FontEvictionPolicy.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/fontLoadQueue/FontLoadQueue.test.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/fontLoadQueue/FontLoadQueue.test.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/fontLoadQueue/FontLoadQueue.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/fontLoadQueue/FontLoadQueue.test.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/fontLoadQueue/FontLoadQueue.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/fontLoadQueue/FontLoadQueue.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/fontLoadQueue/FontLoadQueue.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/fontLoadQueue/FontLoadQueue.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/generateFontKey/generateFontKey.test.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/generateFontKey/generateFontKey.test.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/generateFontKey/generateFontKey.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/generateFontKey/generateFontKey.test.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/generateFontKey/generateFontKey.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/generateFontKey/generateFontKey.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/generateFontKey/generateFontKey.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/generateFontKey/generateFontKey.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/getEffectiveConcurrency/getEffectiveConcurrency.test.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/getEffectiveConcurrency/getEffectiveConcurrency.test.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/getEffectiveConcurrency/getEffectiveConcurrency.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/getEffectiveConcurrency/getEffectiveConcurrency.test.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/getEffectiveConcurrency/getEffectiveConcurrency.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/getEffectiveConcurrency/getEffectiveConcurrency.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/getEffectiveConcurrency/getEffectiveConcurrency.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/getEffectiveConcurrency/getEffectiveConcurrency.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/index.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/index.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/index.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/index.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/loadFont/loadFont.test.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/loadFont/loadFont.test.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/loadFont/loadFont.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/loadFont/loadFont.test.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/loadFont/loadFont.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/loadFont/loadFont.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/loadFont/loadFont.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/loadFont/loadFont.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/yieldToMainThread/yieldToMainThread.test.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/yieldToMainThread/yieldToMainThread.test.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/yieldToMainThread/yieldToMainThread.test.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/yieldToMainThread/yieldToMainThread.test.ts diff --git a/src/entities/Font/model/store/appliedFontsStore/utils/yieldToMainThread/yieldToMainThread.ts b/src/entities/Font/model/store/fontLifecycleManager/utils/yieldToMainThread/yieldToMainThread.ts similarity index 100% rename from src/entities/Font/model/store/appliedFontsStore/utils/yieldToMainThread/yieldToMainThread.ts rename to src/entities/Font/model/store/fontLifecycleManager/utils/yieldToMainThread/yieldToMainThread.ts diff --git a/src/entities/Font/model/store/index.ts b/src/entities/Font/model/store/index.ts index bc6e56a..97ebad0 100644 --- a/src/entities/Font/model/store/index.ts +++ b/src/entities/Font/model/store/index.ts @@ -1,9 +1,9 @@ -// Applied fonts manager -export * from './appliedFontsStore/appliedFontsStore.svelte'; +// Font lifecycle manager (browser-side load + cache + eviction) +export * from './fontLifecycleManager/fontLifecycleManager.svelte'; -// Single FontStore +// Paginated catalog export { - createFontStore, - FontStore, - fontStore, -} from './fontStore/fontStore.svelte'; + createFontCatalogStore, + FontCatalogStore, + fontCatalogStore, +} from './fontCatalogStore/fontCatalogStore.svelte'; diff --git a/src/entities/Font/model/types/index.ts b/src/entities/Font/model/types/index.ts index f4edb26..b23390c 100644 --- a/src/entities/Font/model/types/index.ts +++ b/src/entities/Font/model/types/index.ts @@ -23,5 +23,5 @@ export type { FontCollectionState, } from './store'; -export * from './store/appliedFonts'; +export * from './store/fontLifecycle'; export * from './typography'; diff --git a/src/entities/Font/model/types/store/appliedFonts.ts b/src/entities/Font/model/types/store/fontLifecycle.ts similarity index 100% rename from src/entities/Font/model/types/store/appliedFonts.ts rename to src/entities/Font/model/types/store/fontLifecycle.ts diff --git a/src/entities/Font/ui/FontApplicator/FontApplicator.stories.svelte b/src/entities/Font/ui/FontApplicator/FontApplicator.stories.svelte index ff14ba7..0e370a6 100644 --- a/src/entities/Font/ui/FontApplicator/FontApplicator.stories.svelte +++ b/src/entities/Font/ui/FontApplicator/FontApplicator.stories.svelte @@ -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.', }, }, }} diff --git a/src/entities/Font/ui/FontApplicator/FontApplicator.svelte b/src/entities/Font/ui/FontApplicator/FontApplicator.svelte index fe12255..ae1e97c 100644 --- a/src/entities/Font/ui/FontApplicator/FontApplicator.svelte +++ b/src/entities/Font/ui/FontApplicator/FontApplicator.svelte @@ -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, diff --git a/src/entities/Font/ui/FontVirtualList/FontVirtualList.stories.svelte b/src/entities/Font/ui/FontVirtualList/FontVirtualList.stories.svelte index caf0107..460dfbf 100644 --- a/src/entities/Font/ui/FontVirtualList/FontVirtualList.stories.svelte +++ b/src/entities/Font/ui/FontVirtualList/FontVirtualList.stories.svelte @@ -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 }`.', }, }, }} diff --git a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte index 7d4cc84..9cb6bed 100644 --- a/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte +++ b/src/entities/Font/ui/FontVirtualList/FontVirtualList.svelte @@ -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([]); 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} { }); /** - * Mirror filter selections + debounced search query into fontStore params. - * untrack the write so fontStore's internal $state reads don't feed back + * Mirror filter selections + debounced search query into fontCatalogStore params. + * untrack the write so fontCatalogStore's internal $state reads don't feed back * into this effect's dependency graph. */ $effect(() => { const params = mapAppliedFiltersToParams(appliedFilterStore); - untrack(() => fontStore.setParams(params)); + untrack(() => fontCatalogStore.setParams(params)); }); /** - * Mirror sort selection into fontStore. + * Mirror sort selection into fontCatalogStore. */ $effect(() => { const apiSort = sortStore.apiValue; - untrack(() => fontStore.setSort(apiSort)); + untrack(() => fontCatalogStore.setSort(apiSort)); }); }); diff --git a/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts b/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts index d1faa3e..e4051cd 100644 --- a/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts +++ b/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts @@ -58,7 +58,7 @@ export interface VirtualizerOptions { * when those values change, `offsets` and `totalSize` recompute instantly. * * For font preview rows, pass a closure that reads - * `appliedFontsManager.statuses` so the virtualizer recalculates heights + * `fontLifecycleManager.statuses` so the virtualizer recalculates heights * as fonts finish loading, eliminating the DOM-measurement snap on load. */ estimateSize: (index: number) => number; diff --git a/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts b/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts index 1c6bc3c..124ba24 100644 --- a/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts +++ b/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts @@ -16,8 +16,8 @@ import { type FontLoadRequestConfig, type UnifiedFont, - appliedFontsManager, - fontStore, + fontCatalogStore, + fontLifecycleManager, getFontUrl, } from '$entities/Font'; import { typographySettingsStore } from '$features/AdjustTypography/model'; @@ -140,7 +140,7 @@ export class ComparisonStore { }); if (configs.length > 0) { - appliedFontsManager.touch(configs); + fontLifecycleManager.touch(configs); this.#checkFontsLoaded(); } }); @@ -151,7 +151,7 @@ export class ComparisonStore { return; } - const fonts = fontStore.fonts; + const fonts = fontCatalogStore.fonts; if (fonts.length >= 2) { untrack(() => { const id1 = fonts[0].id; @@ -168,17 +168,17 @@ export class ComparisonStore { const fb = this.#fontB; const w = typographySettingsStore.weight; if (fa) { - appliedFontsManager.pin(fa.id, w, fa.features?.isVariable); + fontLifecycleManager.pin(fa.id, w, fa.features?.isVariable); } if (fb) { - appliedFontsManager.pin(fb.id, w, fb.features?.isVariable); + fontLifecycleManager.pin(fb.id, w, fb.features?.isVariable); } return () => { if (fa) { - appliedFontsManager.unpin(fa.id, w, fa.features?.isVariable); + fontLifecycleManager.unpin(fa.id, w, fa.features?.isVariable); } if (fb) { - appliedFontsManager.unpin(fb.id, w, fb.features?.isVariable); + fontLifecycleManager.unpin(fb.id, w, fb.features?.isVariable); } }; }); diff --git a/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts b/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts index 5a3146e..21fb026 100644 --- a/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts +++ b/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts @@ -55,8 +55,8 @@ vi.mock('$entities/Font', async importOriginal => { const actual = await importOriginal(); return { ...actual, - fontStore: { fonts: [] }, - appliedFontsManager: { + fontCatalogStore: { fonts: [] }, + fontLifecycleManager: { touch: vi.fn(), pin: vi.fn(), unpin: vi.fn(), @@ -85,8 +85,8 @@ vi.mock('$features/AdjustTypography/model', () => ({ })); import { - appliedFontsManager, - fontStore, + fontCatalogStore, + fontLifecycleManager, } from '$entities/Font'; import * as proxyFonts from '$entities/Font/api/proxy/proxyFonts'; import { ComparisonStore } from './comparisonStore.svelte'; @@ -100,7 +100,7 @@ describe('ComparisonStore', () => { vi.clearAllMocks(); mockStorage._value = { fontAId: null, fontBId: null }; mockStorage._clear.mockClear(); - (fontStore as any).fonts = []; + (fontCatalogStore as any).fonts = []; // Default: fetchFontsByIds returns empty so tests that don't care don't hang vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([]); @@ -155,7 +155,7 @@ describe('ComparisonStore', () => { describe('Default Fallbacks', () => { it('should update storage with default IDs when storage is empty', async () => { - (fontStore as any).fonts = [mockFontA, mockFontB]; + (fontCatalogStore as any).fonts = [mockFontA, mockFontB]; vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([mockFontA, mockFontB]); new ComparisonStore(); @@ -212,12 +212,12 @@ describe('ComparisonStore', () => { new ComparisonStore(); await vi.waitFor(() => { - expect(appliedFontsManager.pin).toHaveBeenCalledWith( + expect(fontLifecycleManager.pin).toHaveBeenCalledWith( mockFontA.id, 400, mockFontA.features?.isVariable, ); - expect(appliedFontsManager.pin).toHaveBeenCalledWith( + expect(fontLifecycleManager.pin).toHaveBeenCalledWith( mockFontB.id, 400, mockFontB.features?.isVariable, @@ -238,12 +238,12 @@ describe('ComparisonStore', () => { store.fontA = mockFontC; await vi.waitFor(() => { - expect(appliedFontsManager.unpin).toHaveBeenCalledWith( + expect(fontLifecycleManager.unpin).toHaveBeenCalledWith( mockFontA.id, 400, mockFontA.features?.isVariable, ); - expect(appliedFontsManager.pin).toHaveBeenCalledWith( + expect(fontLifecycleManager.pin).toHaveBeenCalledWith( mockFontC.id, 400, mockFontC.features?.isVariable, diff --git a/src/widgets/ComparisonView/ui/FontList/FontList.svelte b/src/widgets/ComparisonView/ui/FontList/FontList.svelte index 5bb6061..70b9fb7 100644 --- a/src/widgets/ComparisonView/ui/FontList/FontList.svelte +++ b/src/widgets/ComparisonView/ui/FontList/FontList.svelte @@ -9,8 +9,8 @@ import { FontVirtualList, type UnifiedFont, VIRTUAL_INDEX_NOT_LOADED, - appliedFontsManager, - fontStore, + fontCatalogStore, + fontLifecycleManager, } from '$entities/Font'; import { getSkeletonWidth } from '$shared/lib/utils'; import { @@ -36,7 +36,7 @@ function getVirtualIndex(fontId: string | undefined): number { if (!fontId) { return -1; } - const idx = fontStore.fonts.findIndex(f => f.id === fontId); + const idx = fontCatalogStore.fonts.findIndex(f => f.id === fontId); if (idx === -1) { return VIRTUAL_INDEX_NOT_LOADED; } @@ -77,11 +77,11 @@ function handleSelect(font: UnifiedFont) { /** * Returns true once the font file is loaded (or errored) and safe to render. * Called inside the template — Svelte 5 tracks the $state reads inside - * appliedFontsManager.getFontStatus(), so each row re-renders reactively + * fontLifecycleManager.getFontStatus(), so each row re-renders reactively * when its file arrives. */ function isFontReady(font: UnifiedFont): boolean { - const status = appliedFontsManager.getFontStatus( + const status = fontLifecycleManager.getFontStatus( font.id, DEFAULT_FONT_WEIGHT, font.features?.isVariable, diff --git a/src/widgets/ComparisonView/ui/Search/Search.svelte b/src/widgets/ComparisonView/ui/Search/Search.svelte index 388412e..73bc079 100644 --- a/src/widgets/ComparisonView/ui/Search/Search.svelte +++ b/src/widgets/ComparisonView/ui/Search/Search.svelte @@ -2,7 +2,7 @@ Component: Search Typeface search input for the comparison view. Writes through appliedFilterStore; the global bridge in $features/FilterAndSortFonts - propagates the value into fontStore. + propagates the value into fontCatalogStore. -->