Commit Graph

132 Commits

Author SHA1 Message Date
Ilia Mashkov 0aae710e35 fix: dispose persistent-store effects; close comparisonStore effect leak
Thread the new createPersistentStore.destroy() through its owners so the
save effect.root is actually torn down: LayoutManager gains destroy();
ThemeManager and typographySettings dispose their #store in their existing
destroy().

comparisonStore had its own leak — a constructor $effect.root whose disposer
was discarded, and a module-level persistent storage created at import. Move
storage into the instance (#storage, created lazily per instance), capture
the effect.root disposer, add destroy(), and adopt createSingleton so reset
runs resetAll() + destroy(). Re-export createSingleton from the $shared/lib
barrel; give the test storage mock a destroy().
2026-06-03 10:16:47 +03:00
Ilia Mashkov ded9606c30 fix(shared): give createPersistentStore a destroy() to dispose its effect.root
The store created an $effect.root for the save-on-change sync but returned no
disposer, so the effect leaked for the life of the process — contradicting
the rule that $effect.root owners must expose destroy(). Capture and expose
the disposer.

- add destroy() to the returned store; covered by tests (flushSync proves the
  save effect runs before destroy and stops after)
- trim the bloated header (two near-duplicate @example blocks) to one concise
  JSDoc — no fluff
- update typographySettings test mocks to satisfy the now-required destroy()

Consumers (LayoutManager, ThemeManager, typographySettings, comparisonStore)
do not yet call it — threading + the createSingleton migration follow.
2026-06-03 10:00:21 +03:00
Ilia Mashkov f0736f4d35 feat(shared): add createSingleton lazy-singleton accessor helper
Standardizes the getX() / __resetX() pattern hand-rolled identically across
every store: lazy construction on first get(), memoized thereafter, and a
reset() that runs an optional teardown (e.g. destroy()) and clears so the
next get() rebuilds. Lazy by construction, so owning modules stay inert at
import. Covered by unit tests (laziness, memoization, rebuild-after-reset,
teardown-once-with-live-instance, reset-before-get no-op, falsy-value
caching).

Not yet adopted by the stores — that migration is a separate step.
2026-06-03 09:39:51 +03:00
Ilia Mashkov a428eac309 docs: remove decorative separator comments
Strip box-drawing (──) section dividers and ===/banner headers — visual
noise with no information. Where a divider label carried a non-obvious
why (VirtualList owns scrolling; mobile footer is md:hidden because header
stats take over) it is kept as a plain one-line comment; pure restatements
of the markup (Header bar, Red hover line, Bottom: fixed controls) are
dropped. Single comment style, no fluff.
2026-06-03 08:45:36 +03:00
Ilia Mashkov 60e115309c refactor(shared/api): lazify queryClient to remove eager-construction footgun
queryClient.ts constructed the TanStack client at module eval but was not
in the sideEffects allowlist, so Rollup treated the module as pure — safe
only while a value-importer keeps it alive (D-3).

- replace the eager `queryClient` singleton with a memoized getQueryClient()
  factory; construction is deferred to first call
- the module is now genuinely side-effect-free, so no sideEffects exception
  is needed and construction can never be legally tree-shaken away
- route all consumers through getQueryClient() (QueryProvider as first
  caller; stores via class fields/observers; tests via a local alias; the
  fontCatalogStore spec mock now overrides getQueryClient)
2026-06-02 23:13:03 +03:00
Ilia Mashkov c6c8497906 fix: break import cycles
import/no-cycle (now active) flagged 17 cycles across 12 files:

- shared/ui self-barrel cycles (Logo/Stat/StatGroup/ComboControl/
  FilterGroup/SectionHeader): import siblings relatively instead of
  through the $shared/ui barrel that re-exports them
- shared/lib/utils: roundToStepPrecision imports getDecimalPlaces
  relatively instead of via the utils barrel
- routes: lazy-load Redirect in the router so it no longer statically
  imports a component that imports navigate back from it
2026-06-02 23:02:07 +03:00
Ilia Mashkov f3a10e38df refactor: clear remaining lint errors (comma operator, bind:this ref)
- splitArray: replace the comma-operator reduce body with an explicit
  block + return (no-sequences); behaviour unchanged
- BreadcrumbHeaderSeeded: declare the bind:this ref with $state() so it
  is not flagged as never-assigned (oxlint cannot see template bindings),
  matching the rest of the codebase; guard the onMount use
2026-06-02 23:01:59 +03:00
Ilia Mashkov 9788f07dec refactor: remove unused vars and dead code
Cleanup surfaced once the oxlint config actually loads (no-unused-vars).

- drop dead locals/imports/params (cachedOffsetTop, elasticOut, key,
  unused type imports, unused test imports; _-prefix unused mock params)
- createVirtualizer: keep the _version read (reactive subscription inside
  $derived.by) but bind it to _v so it is not flagged
- scrollBreadcrumbsStore.test: keep the removeEventListener mock side
  effect, drop the unread spy binding
2026-06-02 23:01:48 +03:00
Ilia Mashkov c72b51b1c7 refactor(shared): keep BaseQueryStore out of the lib barrels
BaseQueryStore pulls @tanstack/query-core. Re-exporting it through the
broad $shared/lib and $shared/lib/helpers barrels made every consumer of
those barrels eager-load TanStack at module-eval time (no tree-shaking in
vitest/vite-node), which is what surfaced the queryClient mock init-order
failure. Its single consumer now imports it by path.
2026-05-31 19:16:44 +03:00
Ilia Mashkov 4d8dcf52e0 refactor: move BaseQueryStore into separate directory, adjust exports/imports 2026-05-31 17:33:06 +03:00
Ilia Mashkov 907145c655 refactor(shared): drop createTypographyControl from shared/lib 2026-05-31 17:08:55 +03:00
Ilia Mashkov 7834c7cbf2 refactor(AdjustTypography): add typography-control module (factory, types, constants) 2026-05-31 16:52:37 +03:00
Ilia Mashkov ecdb2f1b7f refactor(shared): remove deprecated TextLayoutEngine and its re-exports 2026-05-31 13:36:39 +03:00
Ilia Mashkov ddadac8686 reafactor: move CharacterComparisonEngine into Font entity 2026-05-30 18:48:53 +03:00
Ilia Mashkov 06b6274e66 refactor: extract magic constants — wave 5 (single-site thresholds)
Long-tail cleanup: threshold and default-value literals in shared
helpers get named module-level constants.

- CharacterComparisonEngine: CHAR_PROXIMITY_RANGE_PCT (5),
  DEFAULT_RENDER_SIZE_PX (16) — kept local instead of importing
  DEFAULT_FONT_SIZE from \$entities/Font because \$shared/lib cannot
  legally upward-import from \$entities per FSD (also avoids an init
  cycle through the mocks barrel).
- typographySettingsStore: BASE_SIZE_EPSILON (0.01) — rounding-jitter
  guard for baseSize reconciliation.
- createDebouncedState: DEFAULT_DEBOUNCE_MS (300) — exported so callers
  can mirror the default.
- createVirtualizer: MEASUREMENT_EPSILON_PX (0.5) — minimum height
  delta before committing a re-measured row.
- createPerspectiveManager: PERSPECTIVE_TOGGLE_THRESHOLD (0.5) — the
  halfway point on the 0-1 spring that flips isBack/isFront.

Skipped #19 (PerspectivePlan defaults) per review — marginal gain.
2026-05-24 22:07:44 +03:00
Ilia Mashkov 0c59262a59 refactor: extract magic constants — wave 4 (UX timings + physics)
Name throttle/debounce intervals, spring presets, and layout paddings
that were inline numeric literals:

- VirtualList: VISIBLE_CHANGE_THROTTLE_MS (150), NEAR_BOTTOM_THROTTLE_MS
  (200), JUMP_THROTTLE_MS (200)
- SampleList: CHECK_POSITION_THROTTLE_MS (100)
- SliderArea: SLIDER_SPRING_CONFIG ({stiffness: 0.2, damping: 0.7}),
  SLIDER_PERSIST_DEBOUNCE_MS (100), SLIDER_PADDING_MOBILE_PX (48),
  SLIDER_PADDING_DESKTOP_PX (96)
- FontVirtualList: TOUCH_DEBOUNCE_MS (150)
- createPerspectiveManager: PERSPECTIVE_SPRING_CONFIG ({stiffness: 0.2,
  damping: 0.8})

No behavior changes — values preserved exactly.
2026-05-24 21:13:46 +03:00
Ilia Mashkov f92577608a refactor(Font): use pretext layout() directly in row size resolver
createFontRowSizeResolver was reaching into TextLayoutEngine, which
internally called pretext's heavy layoutWithLines and then walked
per-grapheme cursors to build chars arrays. The resolver discarded all
that work and used only totalHeight.

Replace with direct prepare + layout from @chenglou/pretext — pretext
docs explicitly recommend layout() (not layoutWithLines) for the resize
hot path: pure arithmetic on cached segment widths, no canvas calls, no
string allocations.

Test spies on TextLayoutEngine.prototype.layout migrated to vi.mock-ed
pretext layout (pretext's ESM exports are frozen — vi.spyOn fails with
"Cannot redefine property").

TextLayoutEngine marked @deprecated since it has no remaining
consumers; slated for removal after a release cycle.
2026-05-24 20:12:48 +03:00
Ilia Mashkov 728380498b 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.
2026-05-24 20:00:43 +03:00
Ilia Mashkov 54087b7b2a feat: replace clsx with cn util 2026-04-23 14:59:32 +03:00
Ilia Mashkov 2c579a3336 feat(shared): add cn utility for tailwind-aware class merging 2026-04-23 14:59:32 +03:00
Ilia Mashkov 2803bcd22c fix(createVirtualizer): add window check to resolve the ReferenceError 2026-04-23 14:16:06 +03:00
Ilia Mashkov a801903fd3 feat(FontList): use getSkeletonWidth utility for skeleton row widths 2026-04-22 09:02:32 +03:00
Ilia Mashkov 836b83f75d style: apply new dprint rules to CharacterComparisonEngine 2026-04-20 11:06:54 +03:00
Ilia Mashkov 141126530d fix(ComparisonView): fix character morphing thresholds and add tracking support 2026-04-20 10:52:28 +03:00
Ilia Mashkov af05443763 chore(storybook): purge unused Providers props 2026-04-18 11:02:34 +03:00
Ilia Mashkov 8a93c7b545 chore: purge shadcn from codebase. Replace with bits-ui components and other tools 2026-04-17 13:37:44 +03:00
Ilia Mashkov fb1d2765d0 chore: purge TooltipProvider 2026-04-17 13:20:01 +03:00
Ilia Mashkov 12e8bc0a89 chore: enforce brackets for if clause and for/while loops 2026-04-17 13:05:36 +03:00
Ilia Mashkov cfaff46d59 chore: follow the general comments style 2026-04-17 12:14:55 +03:00
Ilia Mashkov 0a489a8adc fix(BaseQueryStore): use QueryObserverOptions instead of QueryOptions
Workflow / build (pull_request) Successful in 58s
Workflow / publish (pull_request) Has been skipped
QueryOptions has queryKey as optional; QueryObserverOptions requires it,
matching what QueryObserver.constructor and setOptions actually expect.
2026-04-15 22:37:30 +03:00
Ilia Mashkov 10f4781a67 test: enrich coverage for queryKeys, BaseQueryStore, and BatchFontStore
- queryKeys: add mutation-safety test for batch(), key hierarchy tests
  (list/batch/detail keys rooted in their parent base keys), and
  unique-key test for different detail IDs
- BaseQueryStore: add initial-state test (data undefined, isError false
  before any fetch resolves)
- BatchFontStore: add FontResponseError type assertion on malformed
  response, null error assertion on success, and setIds([]) disables
  query and returns empty fonts without triggering a fetch
2026-04-15 15:59:01 +03:00
Ilia Mashkov 8e88d1b7cf feat: add BaseQueryStore for reactive query observers 2026-04-15 12:19:25 +03:00
Ilia Mashkov 46b9db1db3 feat: export ItemSizeResolver type and document reactive estimateSize contract 2026-04-12 19:43:44 +03:00
Ilia Mashkov 4b017a83bb fix: add missing JSDoc, return types, and as-any comments to layout engines 2026-04-12 09:51:36 +03:00
Ilia Mashkov 338ca9b4fd feat: export TextLayoutEngine and CharacterComparisonEngine from shared helpers index
Remove deleted createCharacterComparison exports and benchmark.
2026-04-11 16:44:49 +03:00
Ilia Mashkov 5977e0a0dc fix: correct advances null-check in CharacterComparisonEngine and remove unused TextLayoutEngine dep 2026-04-11 16:14:28 +03:00
Ilia Mashkov 2b0d8470e5 test: fix CharacterComparisonEngine tests — correct env directive, canvas mock, and full spec coverage 2026-04-11 16:14:24 +03:00
Ilia Mashkov 351ee9fd52 docs: add inline documentation to TextLayoutEngine 2026-04-11 16:10:01 +03:00
Ilia Mashkov a526a51af8 test: fix TextLayoutEngine tests — correct jsdom directive placement and canvas mock setup
fix: correct grapheme-width fallback in TextLayoutEngine for null breakableFitAdvances
2026-04-11 15:48:52 +03:00
Ilia Mashkov fcde78abad test: add canvas mock helper for pretext-based engine tests 2026-04-11 15:48:47 +03:00
Ilia Mashkov bc4ab58644 fix(buildQueryString): change the way the searchParams built 2026-04-03 16:08:15 +03:00
Ilia Mashkov 13818d5844 refactor(shared): update utilities, API layer, and types 2026-03-02 22:19:13 +03:00
Ilia Mashkov ac73fd5044 refactor(helpers): modernize reactive helpers and add tests 2026-03-02 22:18:59 +03:00
Ilia Mashkov 80feda41a3 feat(createResponsiveManager): rewrote ifs to switch case 2026-02-27 18:35:40 +03:00
Ilia Mashkov 3a813b019b chore: rename 2026-02-27 13:00:58 +03:00
Ilia Mashkov da79dd2e35 feat: storybook cases and mocks 2026-02-19 13:58:12 +03:00
Ilia Mashkov d15b2ffe3f test(createVirtualizer): test coverage for virtual list logic 2026-02-18 20:54:34 +03:00
Ilia Mashkov 51ea8a9902 test(smoothScroll): cast mock to the proper type 2026-02-18 20:40:00 +03:00
Ilia Mashkov e81cadb32a feat(smoothScroll): cover smoothScroll util with unit tests 2026-02-18 20:20:24 +03:00
Ilia Mashkov 1c3908f89e test(createPersistentStore): cover createPersistentStore helper with unit tests 2026-02-18 20:19:47 +03:00