chore: follow the general comments style

This commit is contained in:
Ilia Mashkov
2026-04-17 12:14:55 +03:00
parent 0ebf75b24e
commit cfaff46d59
56 changed files with 1600 additions and 499 deletions

View File

@@ -41,15 +41,25 @@ type ThemeSource = 'system' | 'user';
*/
class ThemeManager {
// Private reactive state
/** Current theme value ('light' or 'dark') */
/**
* Current theme value ('light' or 'dark')
*/
#theme = $state<Theme>('light');
/** Whether theme is controlled by user or follows system */
/**
* Whether theme is controlled by user or follows system
*/
#source = $state<ThemeSource>('system');
/** MediaQueryList for detecting system theme changes */
/**
* MediaQueryList for detecting system theme changes
*/
#mediaQuery: MediaQueryList | null = null;
/** Persistent storage for user's theme preference */
/**
* Persistent storage for user's theme preference
*/
#store = createPersistentStore<Theme | null>('glyphdiff:theme', null);
/** Bound handler for system theme change events */
/**
* Bound handler for system theme change events
*/
#systemChangeHandler = this.#onSystemChange.bind(this);
constructor() {
@@ -64,22 +74,30 @@ class ThemeManager {
}
}
/** Current theme value */
/**
* Current theme value
*/
get value(): Theme {
return this.#theme;
}
/** Source of current theme ('system' or 'user') */
/**
* Source of current theme ('system' or 'user')
*/
get source(): ThemeSource {
return this.#source;
}
/** Whether dark theme is active */
/**
* Whether dark theme is active
*/
get isDark(): boolean {
return this.#theme === 'dark';
}
/** Whether theme is controlled by user (not following system) */
/**
* Whether theme is controlled by user (not following system)
*/
get isUserControlled(): boolean {
return this.#source === 'user';
}

View File

@@ -1,9 +1,9 @@
/** @vitest-environment jsdom */
/**
* @vitest-environment jsdom
*/
// ============================================================
// Mock MediaQueryListEvent for system theme change simulations
// Note: Other mocks (ResizeObserver, localStorage, matchMedia) are set up in vitest.setup.unit.ts
// ============================================================
class MockMediaQueryListEvent extends Event {
matches: boolean;
@@ -16,9 +16,7 @@ class MockMediaQueryListEvent extends Event {
}
}
// ============================================================
// NOW IT'S SAFE TO IMPORT
// ============================================================
import {
afterEach,

View File

@@ -15,19 +15,29 @@ const PROXY_API_URL = 'https://api.glyphdiff.com/api/v1/filters' as const;
* Filter metadata type from backend
*/
export interface FilterMetadata {
/** Filter ID (e.g., "providers", "categories", "subsets") */
/**
* Filter ID (e.g., "providers", "categories", "subsets")
*/
id: string;
/** Display name (e.g., "Font Providers", "Categories", "Character Subsets") */
/**
* Display name (e.g., "Font Providers", "Categories", "Character Subsets")
*/
name: string;
/** Filter description */
/**
* Filter description
*/
description: string;
/** Filter type */
/**
* Filter type
*/
type: 'enum' | 'string' | 'array';
/** Available filter options */
/**
* Available filter options
*/
options: FilterOption[];
}
@@ -35,16 +45,24 @@ export interface FilterMetadata {
* Filter option type
*/
export interface FilterOption {
/** Option ID (e.g., "google", "serif", "latin") */
/**
* Option ID (e.g., "google", "serif", "latin")
*/
id: string;
/** Display name (e.g., "Google Fonts", "Serif", "Latin") */
/**
* Display name (e.g., "Google Fonts", "Serif", "Latin")
*/
name: string;
/** Option value (e.g., "google", "serif", "latin") */
/**
* Option value (e.g., "google", "serif", "latin")
*/
value: string;
/** Number of fonts with this value */
/**
* Number of fonts with this value
*/
count: number;
}
@@ -52,7 +70,9 @@ export interface FilterOption {
* Proxy filters API response
*/
export interface ProxyFiltersResponse {
/** Array of filter metadata */
/**
* Array of filter metadata
*/
filters: FilterMetadata[];
}

View File

@@ -1,15 +1,56 @@
export type {
/**
* Top-level configuration for all filters
*/
FilterConfig,
/**
* Configuration for a single grouping of filter properties
*/
FilterGroupConfig,
} from './types/filter';
export { filtersStore } from './state/filters.svelte';
export { filterManager } from './state/manager.svelte';
/**
* Global reactive filter state
*/
export {
/**
* Low-level property selection store
*/
filtersStore,
} from './state/filters.svelte';
/**
* Main filter controller
*/
export {
/**
* High-level manager for syncing search and filters
*/
filterManager,
} from './state/manager.svelte';
/**
* Sorting logic
*/
export {
/**
* Map of human-readable labels to API sort keys
*/
SORT_MAP,
/**
* List of all available sort options for the UI
*/
SORT_OPTIONS,
/**
* Valid sort key values
*/
type SortApiValue,
/**
* UI model for a single sort option
*/
type SortOption,
/**
* Reactive store for the current sort selection
*/
sortStore,
} from './store/sortStore.svelte';

View File

@@ -32,13 +32,19 @@ import {
* Provides reactive access to filter data
*/
class FiltersStore {
/** TanStack Query result state */
/**
* TanStack Query result state
*/
protected result = $state<QueryObserverResult<FilterMetadata[], Error>>({} as any);
/** TanStack Query observer instance */
/**
* TanStack Query observer instance
*/
protected observer: QueryObserver<FilterMetadata[], Error>;
/** Shared query client */
/**
* Shared query client
*/
protected qc = queryClient;
/**

View File

@@ -21,17 +21,23 @@ function createSortStore(initial: SortOption = 'Popularity') {
let current = $state<SortOption>(initial);
return {
/** Current display label (e.g. 'Popularity') */
/**
* Current display label (e.g. 'Popularity')
*/
get value() {
return current;
},
/** Mapped API value (e.g. 'popularity') */
/**
* Mapped API value (e.g. 'popularity')
*/
get apiValue(): SortApiValue {
return SORT_MAP[current];
},
/** Set the active sort option by its display label */
/**
* Set the active sort option by its display label
*/
set(option: SortOption) {
current = option;
},

View File

@@ -1,12 +1,27 @@
import type { Property } from '$shared/lib';
export interface FilterGroupConfig<TValue extends string> {
/**
* Unique identifier for the filter group (e.g. 'categories')
*/
id: string;
/**
* Human-readable label displayed in the UI header
*/
label: string;
/**
* List of toggleable properties within this group
*/
properties: Property<TValue>[];
}
export interface FilterConfig<TValue extends string> {
/**
* Optional string to filter results by name
*/
queryValue?: string;
/**
* Collection of filter groups to display
*/
groups: FilterGroupConfig<TValue>[];
}

View File

@@ -30,9 +30,12 @@ import { SvelteMap } from 'svelte/reactivity';
type ControlOnlyFields<T extends string = string> = Omit<ControlModel<T>, keyof ControlDataModel>;
/**
* A control with its instance
* A control with its associated instance
*/
export interface Control extends ControlOnlyFields<ControlId> {
/**
* The reactive typography control instance
*/
instance: TypographyControl;
}
@@ -40,9 +43,21 @@ export interface Control extends ControlOnlyFields<ControlId> {
* Storage schema for typography settings
*/
export interface TypographySettings {
/**
* Base font size (User preference, unscaled)
*/
fontSize: number;
/**
* Numeric font weight (100-900)
*/
fontWeight: number;
/**
* Line height multiplier (e.g. 1.5)
*/
lineHeight: number;
/**
* Letter spacing in em/px
*/
letterSpacing: number;
}
@@ -53,13 +68,21 @@ export interface TypographySettings {
* responsive scaling support for font size.
*/
export class TypographySettingsManager {
/** Map of controls keyed by ID */
/**
* Internal map of reactive controls keyed by their identifier
*/
#controls = new SvelteMap<string, Control>();
/** Responsive multiplier for font size display */
/**
* Global multiplier for responsive font size scaling
*/
#multiplier = $state(1);
/** Persistent storage for settings */
/**
* LocalStorage-backed storage for persistence
*/
#storage: PersistentStore<TypographySettings>;
/** Base font size (user preference, unscaled) */
/**
* The underlying font size before responsive scaling is applied
*/
#baseSize = $state(DEFAULT_FONT_SIZE);
constructor(configs: ControlModel<ControlId>[], storage: PersistentStore<TypographySettings>) {
@@ -131,16 +154,15 @@ export class TypographySettingsManager {
return 0;
}
/** Current multiplier for responsive scaling */
/**
* Active scaling factor for the rendered font size
*/
get multiplier() {
return this.#multiplier;
}
/**
* Set the multiplier and update font size display
*
* When multiplier changes, the font size control's display value
* is updated to reflect the new scale while preserving base size.
* Updates the multiplier and recalculates dependent control values
*/
set multiplier(value: number) {
if (this.#multiplier === value) return;
@@ -154,14 +176,15 @@ export class TypographySettingsManager {
}
/**
* The scaled size for CSS usage
* Returns baseSize * multiplier for actual rendering
* The actual pixel value for CSS font-size (baseSize * multiplier)
*/
get renderedSize() {
return this.#baseSize * this.#multiplier;
}
/** The base size (User Preference) */
/**
* The raw font size preference before scaling
*/
get baseSize() {
return this.#baseSize;
}
@@ -173,45 +196,63 @@ export class TypographySettingsManager {
}
/**
* Getters for controls
* List of all managed typography controls
*/
get controls() {
return Array.from(this.#controls.values());
}
/**
* Reactive instance for weight manipulation
*/
get weightControl() {
return this.#controls.get('font_weight')?.instance;
}
/**
* Reactive instance for size manipulation
*/
get sizeControl() {
return this.#controls.get('font_size')?.instance;
}
/**
* Reactive instance for line-height manipulation
*/
get heightControl() {
return this.#controls.get('line_height')?.instance;
}
/**
* Reactive instance for letter-spacing manipulation
*/
get spacingControl() {
return this.#controls.get('letter_spacing')?.instance;
}
/**
* Getters for values (besides font-size)
* Current numeric font weight (reactive)
*/
get weight() {
return this.#controls.get('font_weight')?.instance.value ?? DEFAULT_FONT_WEIGHT;
}
/**
* Current numeric line height (reactive)
*/
get height() {
return this.#controls.get('line_height')?.instance.value ?? DEFAULT_LINE_HEIGHT;
}
/**
* Current numeric letter spacing (reactive)
*/
get spacing() {
return this.#controls.get('letter_spacing')?.instance.value ?? DEFAULT_LETTER_SPACING;
}
/**
* Reset all controls to default values
* Reset all controls to project-defined defaults
*/
reset() {
this.#storage.clear();

View File

@@ -1,4 +1,6 @@
/** @vitest-environment jsdom */
/**
* @vitest-environment jsdom
*/
import {
DEFAULT_FONT_SIZE,
DEFAULT_FONT_WEIGHT,