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.
The proxy returned `{fonts: null, total: 0}` for empty results, which
fetchProxyFonts surfaced as a generic Error. fontCatalogStore wrapped it
as FontNetworkError, and TanStack retried 3× with exponential backoff —
pinning the loading skeleton for ~7s before settling on an empty list.
Schema mismatches are deterministic; retrying only delays surfacing the
contract violation.
- shared/api/queryClient: introduce NonRetryableError marker class.
The default retry handler short-circuits when it sees this so any
store using the shared client gets fail-fast behavior for free.
- entities/Font/lib/errors: FontResponseError extends NonRetryableError.
- entities/Font/api/proxy/proxyFonts: throw FontResponseError (was a
bare Error). Document that ProxyFontsResponse.fonts is always an array.
- entities/Font/.../fontCatalogStore.fetchPage: preserve a
FontResponseError raised lower in the stack instead of re-wrapping
it as FontNetworkError.
- features/FilterAndSortFonts/api/filters: throw NonRetryableError on
invalid filters payloads and document the array-never-null contract.
Dark mode unchanged. Targets that were reported as "barely visible" in
light theme:
Surfaces / dividers
- --color-border-subtle (light) bumped from rgb(0 0 0 / 0.05) to
--neutral-300 (matches the Input underline variant's border color and
yields a visible hairline on bg-surface / bg-paper).
- New bg-subtle utility (same color as border-subtle but as
background-color) — used by Divider component and the TypographyMenu
inline column separator. Replaces ad-hoc 'bg-black/5 dark:bg-white/10'
and 'bg-black/10 dark:bg-white/10' bands.
- FontSearch + ComparisonView Search wrapper borders switched from
hand-written 'border-swiss-black/5 dark:border-white/10' to
border-subtle so they participate in the palette.
Muted text
- Button tertiary inactive text (light) bumped neutral-400 → neutral-600
(~2.7:1 → ~7.5:1 contrast). Covers the A/B toggle and the font-list
rows in the sidebar.
- Label/TechText muted variant (light) bumped neutral-400 → neutral-600.
Covers the ComboControl value text.
- Link text aligned to neutral-500 / neutral-400 (subtle but visible).
No behavior changes; pure styling.
Replace inline class clusters with the design-system utilities and
tokens established in the prior two commits. No behavior changes
intended beyond two real bug fixes.
Bug fixes:
- SampleList.svelte: 'border-border-subtle bg-background-40' was a
silent no-op (both classes mis-spelled). Now 'border-subtle
bg-background/40' applies as intended.
- FontList.svelte: 'h-[44px]' → 'h-11' (44px = 2.75rem = spacing-11,
no need for arbitrary value).
Sweeps:
- TypographyMenu: popover + floating bar now use surface-popover /
surface-floating + shadow-popover.
- FontList + FilterGroup: tertiary list buttons use the new
Button layout="block-list-row" variant; skeleton fills use
the skeleton-fill utility.
- Footer / BreadcrumbHeader: surface-floating absorbs the
bg-surface/blur/border cluster. Footer bumped to z-20 with a
comment explaining the stacking against SidebarContainer (z-40/50).
- FontSampler: surface-card + hover shadow-stamp-card token.
- SliderArea: surface-canvas, flex-center, shadow-floating-panel
tokens (light + dark variants).
- Sidebar / Header / ButtonGroup / Layout / SidebarContainer:
bg-surface dark:bg-dark-bg → surface-canvas (8 sites);
SidebarContainer mobile panel uses shadow-overlay.
- Loader / Thumb: flex items-center justify-center → flex-center;
Thumb durations → duration-fast.
- ComboControl: trigger uses surface-card-elevated when open,
popover uses surface-card-elevated, label cluster → text-label-mono,
flex-center for the trigger interior.
- Slider: shadow-sm → shadow-rest, duration-150 → duration-fast.
- text-secondary → text-subtle across Input, Slider, ComboControl
(matches the rename in the styles commit).
- Link: reverted earlier surface-floating attempt — Link's original
bg-surface/80 backdrop-blur pattern was thinner than surface-floating
(no border, smaller blur), and the Footer was overlaying its own
border-subtle on top, fighting the utility. Kept the original style.
- New layout prop with values 'inline' (default) and 'block-list-row'.
The block-list-row variant bakes in full-width, left-aligned content
with trailing icon and text-sm, replacing the ~10-class override
duplicated across FilterGroup, FontList, and similar list-row sites.
- primary variant's three hard-offset shadows now reference the
shadow-stamp-{rest,hover,pressed} tokens; the 0.0625rem translate
becomes translate-{x,y}-px.
- Base classes use text-label-mono and duration-normal utilities
instead of inline 'font-primary font-bold tracking-tight uppercase'
and 'duration-200'.
- The icon variant's background uses surface-canvas (semantic naming;
picks up dark-mode automatically via --color-surface).
- text-secondary → text-subtle (avoids collision with the @theme
--color-secondary token; see earlier styles commit).
New exported type: ButtonLayout.
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.
- Use existing MULTIPLIER_S/M/L from \$entities/Font in SliderArea instead
of inlining the 0.5/0.75/1 literals (constants already existed but were
duplicated at the call site).
- Centralize API base URL in \$shared/api/endpoints.ts (was duplicated
between proxyFonts and FilterAndSortFonts filters api).
- Promote every 'glyphdiff:...' localStorage key to a named module-level
STORAGE_KEY constant. Test files now import the source constant rather
than redeclaring it (eliminates silent-typo divergence risk).
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.
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.