Compare commits

...

4 Commits

17 changed files with 151 additions and 163 deletions

View File

@@ -85,19 +85,11 @@ onDestroy(() => themeManager.destroy());
theme === 'dark' ? 'dark' : '', theme === 'dark' ? 'dark' : '',
)} )}
> >
<header>
<BreadcrumbHeader />
</header>
<!-- <ScrollArea class="h-screen w-screen"> -->
<!-- <main class="flex-1 w-full mx-auto relative"> -->
<TooltipProvider> <TooltipProvider>
{#if fontsReady} {#if fontsReady}
{@render children?.()} {@render children?.()}
{/if} {/if}
</TooltipProvider> </TooltipProvider>
<!-- </main> -->
<!-- </ScrollArea> -->
<footer></footer> <footer></footer>
</div> </div>
</ResponsiveProvider> </ResponsiveProvider>

View File

@@ -35,7 +35,6 @@ const { Story } = defineMeta({
<script lang="ts"> <script lang="ts">
import type { UnifiedFont } from '$entities/Font'; import type { UnifiedFont } from '$entities/Font';
import { controlManager } from '$features/SetupFont';
// Mock fonts for testing // Mock fonts for testing
const mockArial: UnifiedFont = { const mockArial: UnifiedFont = {

View File

@@ -8,14 +8,13 @@ import {
FontApplicator, FontApplicator,
type UnifiedFont, type UnifiedFont,
} from '$entities/Font'; } from '$entities/Font';
import { controlManager } from '$features/SetupFont'; import { typographySettingsStore } from '$features/SetupFont/model';
import { import {
Badge, Badge,
ContentEditable, ContentEditable,
Divider, Divider,
Footnote, Footnote,
Stat, Stat,
StatGroup,
} from '$shared/ui'; } from '$shared/ui';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
@@ -37,11 +36,6 @@ interface Props {
let { font, text = $bindable(), index = 0 }: Props = $props(); let { font, text = $bindable(), index = 0 }: Props = $props();
const fontWeight = $derived(controlManager.weight);
const fontSize = $derived(controlManager.renderedSize);
const lineHeight = $derived(controlManager.height);
const letterSpacing = $derived(controlManager.spacing);
// Adjust the property name to match your UnifiedFont type // Adjust the property name to match your UnifiedFont type
const fontType = $derived((font as any).type ?? (font as any).category ?? ''); const fontType = $derived((font as any).type ?? (font as any).category ?? '');
@@ -52,10 +46,10 @@ const providerBadge = $derived(
); );
const stats = $derived([ const stats = $derived([
{ label: 'SZ', value: `${fontSize}PX` }, { label: 'SZ', value: `${typographySettingsStore.renderedSize}PX` },
{ label: 'WGT', value: `${fontWeight}` }, { label: 'WGT', value: `${typographySettingsStore.weight}` },
{ label: 'LH', value: lineHeight?.toFixed(2) }, { label: 'LH', value: typographySettingsStore.height?.toFixed(2) },
{ label: 'LTR', value: `${letterSpacing}` }, { label: 'LTR', value: `${typographySettingsStore.spacing}` },
]); ]);
</script> </script>
@@ -75,7 +69,7 @@ const stats = $derived([
min-h-60 min-h-60
rounded-none rounded-none
" "
style:font-weight={fontWeight} style:font-weight={typographySettingsStore.weight}
> >
<!-- ── Header bar ─────────────────────────────────────────────────── --> <!-- ── Header bar ─────────────────────────────────────────────────── -->
<div <div
@@ -140,12 +134,12 @@ const stats = $derived([
<!-- ── Main content area ──────────────────────────────────────────── --> <!-- ── Main content area ──────────────────────────────────────────── -->
<div class="flex-1 p-4 sm:p-5 md:p-8 flex items-center overflow-hidden bg-paper dark:bg-dark-card relative z-10"> <div class="flex-1 p-4 sm:p-5 md:p-8 flex items-center overflow-hidden bg-paper dark:bg-dark-card relative z-10">
<FontApplicator {font} weight={fontWeight}> <FontApplicator {font} weight={typographySettingsStore.weight}>
<ContentEditable <ContentEditable
bind:text bind:text
{fontSize} fontSize={typographySettingsStore.renderedSize}
{lineHeight} lineHeight={typographySettingsStore.height}
{letterSpacing} letterSpacing={typographySettingsStore.spacing}
/> />
</FontApplicator> </FontApplicator>
</div> </div>

View File

@@ -1,4 +1,4 @@
export { export {
createTypographyControlManager, createTypographySettingsManager,
type TypographyControlManager, type TypographySettingsManager,
} from './controlManager/controlManager.svelte'; } from './settingsManager/settingsManager.svelte';

View File

@@ -52,7 +52,7 @@ export interface TypographySettings {
* Manages multiple typography controls with persistent storage and * Manages multiple typography controls with persistent storage and
* responsive scaling support for font size. * responsive scaling support for font size.
*/ */
export class TypographyControlManager { export class TypographySettingsManager {
/** Map of controls keyed by ID */ /** Map of controls keyed by ID */
#controls = new SvelteMap<string, Control>(); #controls = new SvelteMap<string, Control>();
/** Responsive multiplier for font size display */ /** Responsive multiplier for font size display */
@@ -242,7 +242,7 @@ export class TypographyControlManager {
* @param storageId - Persistent storage identifier * @param storageId - Persistent storage identifier
* @returns Typography control manager instance * @returns Typography control manager instance
*/ */
export function createTypographyControlManager( export function createTypographySettingsManager(
configs: ControlModel<ControlId>[], configs: ControlModel<ControlId>[],
storageId: string = 'glyphdiff:typography', storageId: string = 'glyphdiff:typography',
) { ) {
@@ -252,5 +252,5 @@ export function createTypographyControlManager(
lineHeight: DEFAULT_LINE_HEIGHT, lineHeight: DEFAULT_LINE_HEIGHT,
letterSpacing: DEFAULT_LETTER_SPACING, letterSpacing: DEFAULT_LETTER_SPACING,
}); });
return new TypographyControlManager(configs, storage); return new TypographySettingsManager(configs, storage);
} }

View File

@@ -15,14 +15,14 @@ import {
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
} from '../../model'; } from '../../model';
import { import {
TypographyControlManager,
type TypographySettings, type TypographySettings,
} from './controlManager.svelte'; TypographySettingsManager,
} from './settingsManager.svelte';
/** /**
* Test Strategy for TypographyControlManager * Test Strategy for TypographySettingsManager
* *
* This test suite validates the TypographyControlManager state management logic. * This test suite validates the TypographySettingsManager state management logic.
* These are unit tests for the manager logic, separate from component rendering. * These are unit tests for the manager logic, separate from component rendering.
* *
* NOTE: Svelte 5's $effect runs in microtasks, so we need to flush effects * NOTE: Svelte 5's $effect runs in microtasks, so we need to flush effects
@@ -45,7 +45,7 @@ async function flushEffects() {
await Promise.resolve(); await Promise.resolve();
} }
describe('TypographyControlManager - Unit Tests', () => { describe('TypographySettingsManager - Unit Tests', () => {
let mockStorage: TypographySettings; let mockStorage: TypographySettings;
let mockPersistentStore: { let mockPersistentStore: {
value: TypographySettings; value: TypographySettings;
@@ -85,7 +85,7 @@ describe('TypographyControlManager - Unit Tests', () => {
describe('Initialization', () => { describe('Initialization', () => {
it('creates manager with default values from storage', () => { it('creates manager with default values from storage', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -105,7 +105,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}; };
mockPersistentStore = createMockPersistentStore(mockStorage); mockPersistentStore = createMockPersistentStore(mockStorage);
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -117,7 +117,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('initializes font size control with base size multiplied by current multiplier (1)', () => { it('initializes font size control with base size multiplied by current multiplier (1)', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -126,7 +126,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('returns all controls via controls getter', () => { it('returns all controls via controls getter', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -142,7 +142,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('returns individual controls via specific getters', () => { it('returns individual controls via specific getters', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -160,7 +160,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('control instances have expected interface', () => { it('control instances have expected interface', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -179,7 +179,7 @@ describe('TypographyControlManager - Unit Tests', () => {
describe('Multiplier System', () => { describe('Multiplier System', () => {
it('has default multiplier of 1', () => { it('has default multiplier of 1', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -188,7 +188,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('updates multiplier when set', () => { it('updates multiplier when set', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -201,7 +201,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('does not update multiplier if set to same value', () => { it('does not update multiplier if set to same value', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -217,7 +217,7 @@ describe('TypographyControlManager - Unit Tests', () => {
mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 }; mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 };
mockPersistentStore = createMockPersistentStore(mockStorage); mockPersistentStore = createMockPersistentStore(mockStorage);
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -241,7 +241,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('updates font size control display value when multiplier increases', () => { it('updates font size control display value when multiplier increases', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -262,7 +262,7 @@ describe('TypographyControlManager - Unit Tests', () => {
describe('Base Size Setter', () => { describe('Base Size Setter', () => {
it('updates baseSize when set directly', () => { it('updates baseSize when set directly', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -273,7 +273,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('updates size control value when baseSize is set', () => { it('updates size control value when baseSize is set', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -284,7 +284,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('applies multiplier to size control when baseSize is set', () => { it('applies multiplier to size control when baseSize is set', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -298,7 +298,7 @@ describe('TypographyControlManager - Unit Tests', () => {
describe('Rendered Size Calculation', () => { describe('Rendered Size Calculation', () => {
it('calculates renderedSize as baseSize * multiplier', () => { it('calculates renderedSize as baseSize * multiplier', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -307,7 +307,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('updates renderedSize when multiplier changes', () => { it('updates renderedSize when multiplier changes', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -320,7 +320,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('updates renderedSize when baseSize changes', () => { it('updates renderedSize when baseSize changes', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -340,7 +340,7 @@ describe('TypographyControlManager - Unit Tests', () => {
// proxy effect behavior should be tested in E2E tests. // proxy effect behavior should be tested in E2E tests.
it('does NOT immediately update baseSize from control change (effect is async)', () => { it('does NOT immediately update baseSize from control change (effect is async)', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -355,7 +355,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('updates baseSize via direct setter (synchronous)', () => { it('updates baseSize via direct setter (synchronous)', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -380,7 +380,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}; };
mockPersistentStore = createMockPersistentStore(mockStorage); mockPersistentStore = createMockPersistentStore(mockStorage);
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -393,7 +393,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('syncs to storage after effect flush (async)', async () => { it('syncs to storage after effect flush (async)', async () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -409,7 +409,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('syncs control changes to storage after effect flush (async)', async () => { it('syncs control changes to storage after effect flush (async)', async () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -422,7 +422,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('syncs height control changes to storage after effect flush (async)', async () => { it('syncs height control changes to storage after effect flush (async)', async () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -434,7 +434,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('syncs spacing control changes to storage after effect flush (async)', async () => { it('syncs spacing control changes to storage after effect flush (async)', async () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -448,7 +448,7 @@ describe('TypographyControlManager - Unit Tests', () => {
describe('Control Value Getters', () => { describe('Control Value Getters', () => {
it('returns current weight value', () => { it('returns current weight value', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -460,7 +460,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('returns current height value', () => { it('returns current height value', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -472,7 +472,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('returns current spacing value', () => { it('returns current spacing value', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -485,7 +485,7 @@ describe('TypographyControlManager - Unit Tests', () => {
it('returns default value when control is not found', () => { it('returns default value when control is not found', () => {
// Create a manager with empty configs (no controls) // Create a manager with empty configs (no controls)
const manager = new TypographyControlManager([], mockPersistentStore); const manager = new TypographySettingsManager([], mockPersistentStore);
expect(manager.weight).toBe(DEFAULT_FONT_WEIGHT); expect(manager.weight).toBe(DEFAULT_FONT_WEIGHT);
expect(manager.height).toBe(DEFAULT_LINE_HEIGHT); expect(manager.height).toBe(DEFAULT_LINE_HEIGHT);
@@ -503,7 +503,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}; };
mockPersistentStore = createMockPersistentStore(mockStorage); mockPersistentStore = createMockPersistentStore(mockStorage);
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -536,7 +536,7 @@ describe('TypographyControlManager - Unit Tests', () => {
clear: clearSpy, clear: clearSpy,
}; };
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -547,7 +547,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('respects multiplier when resetting font size control', () => { it('respects multiplier when resetting font size control', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -565,7 +565,7 @@ describe('TypographyControlManager - Unit Tests', () => {
describe('Complex Scenarios', () => { describe('Complex Scenarios', () => {
it('handles changing multiplier then modifying baseSize', () => { it('handles changing multiplier then modifying baseSize', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -586,7 +586,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('maintains correct renderedSize throughout changes', () => { it('maintains correct renderedSize throughout changes', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -608,7 +608,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('handles multiple control changes in sequence', async () => { it('handles multiple control changes in sequence', async () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -633,7 +633,7 @@ describe('TypographyControlManager - Unit Tests', () => {
mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 }; mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 };
mockPersistentStore = createMockPersistentStore(mockStorage); mockPersistentStore = createMockPersistentStore(mockStorage);
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -645,7 +645,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('handles very small multiplier', () => { it('handles very small multiplier', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -658,7 +658,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('handles large base size with multiplier', () => { it('handles large base size with multiplier', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -671,7 +671,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('handles floating point precision in multiplier', () => { it('handles floating point precision in multiplier', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -690,7 +690,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('handles control methods (increase/decrease)', () => { it('handles control methods (increase/decrease)', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );
@@ -704,7 +704,7 @@ describe('TypographyControlManager - Unit Tests', () => {
}); });
it('handles control boundary conditions', () => { it('handles control boundary conditions', () => {
const manager = new TypographyControlManager( const manager = new TypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA, DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
mockPersistentStore, mockPersistentStore,
); );

View File

@@ -20,5 +20,5 @@ export {
export { export {
type ControlId, type ControlId,
controlManager, typographySettingsStore,
} from './state/manager.svelte'; } from './state/typographySettingsStore';

View File

@@ -1,6 +0,0 @@
import { createTypographyControlManager } from '../../lib';
import { DEFAULT_TYPOGRAPHY_CONTROLS_DATA } from '../const/const';
export type ControlId = 'font_size' | 'font_weight' | 'line_height' | 'letter_spacing';
export const controlManager = createTypographyControlManager(DEFAULT_TYPOGRAPHY_CONTROLS_DATA);

View File

@@ -0,0 +1,8 @@
import { createTypographySettingsManager } from '../../lib';
import { DEFAULT_TYPOGRAPHY_CONTROLS_DATA } from '../const/const';
export type ControlId = 'font_size' | 'font_weight' | 'line_height' | 'letter_spacing';
export const typographySettingsStore = createTypographySettingsManager(
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
'glyphdiff:comparison:typography',
);

View File

@@ -9,7 +9,6 @@
import type { ResponsiveManager } from '$shared/lib'; import type { ResponsiveManager } from '$shared/lib';
import { cn } from '$shared/shadcn/utils/shadcn-utils'; import { cn } from '$shared/shadcn/utils/shadcn-utils';
import { import {
Button,
ComboControl, ComboControl,
ControlGroup, ControlGroup,
Slider, Slider,
@@ -24,7 +23,7 @@ import {
MULTIPLIER_L, MULTIPLIER_L,
MULTIPLIER_M, MULTIPLIER_M,
MULTIPLIER_S, MULTIPLIER_S,
controlManager, typographySettingsStore,
} from '../../model'; } from '../../model';
interface Props { interface Props {
@@ -52,16 +51,16 @@ $effect(() => {
if (!responsive) return; if (!responsive) return;
switch (true) { switch (true) {
case responsive.isMobile: case responsive.isMobile:
controlManager.multiplier = MULTIPLIER_S; typographySettingsStore.multiplier = MULTIPLIER_S;
break; break;
case responsive.isTablet: case responsive.isTablet:
controlManager.multiplier = MULTIPLIER_M; typographySettingsStore.multiplier = MULTIPLIER_M;
break; break;
case responsive.isDesktop: case responsive.isDesktop:
controlManager.multiplier = MULTIPLIER_L; typographySettingsStore.multiplier = MULTIPLIER_L;
break; break;
default: default:
controlManager.multiplier = MULTIPLIER_L; typographySettingsStore.multiplier = MULTIPLIER_L;
} }
}); });
</script> </script>
@@ -133,7 +132,7 @@ $effect(() => {
</div> </div>
<!-- Controls --> <!-- Controls -->
{#each controlManager.controls as control (control.id)} {#each typographySettingsStore.controls as control (control.id)}
<ControlGroup label={control.controlLabel ?? ''}> <ControlGroup label={control.controlLabel ?? ''}>
<Slider <Slider
bind:value={control.instance.value} bind:value={control.instance.value}
@@ -174,7 +173,7 @@ $effect(() => {
</div> </div>
<!-- Controls with dividers between each --> <!-- Controls with dividers between each -->
{#each controlManager.controls as control, i (control.id)} {#each typographySettingsStore.controls as control, i (control.id)}
{#if i > 0} {#if i > 0}
<div class="w-px h-6 md:h-8 bg-black/5 dark:bg-white/10 mx-0.5 md:mx-1 shrink-0"></div> <div class="w-px h-6 md:h-8 bg-black/5 dark:bg-white/10 mx-0.5 md:mx-1 shrink-0"></div>
{/if} {/if}

View File

@@ -21,10 +21,7 @@ import {
fontStore, fontStore,
getFontUrl, getFontUrl,
} from '$entities/Font'; } from '$entities/Font';
import { import { typographySettingsStore } from '$features/SetupFont/model';
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
createTypographyControlManager,
} from '$features/SetupFont';
import { createPersistentStore } from '$shared/lib'; import { createPersistentStore } from '$shared/lib';
import { untrack } from 'svelte'; import { untrack } from 'svelte';
@@ -68,8 +65,8 @@ export class ComparisonStore {
#side = $state<Side>('A'); #side = $state<Side>('A');
/** Slider position for character morphing (0-100) */ /** Slider position for character morphing (0-100) */
#sliderPosition = $state(50); #sliderPosition = $state(50);
/** Typography controls for this comparison */ // /** Typography controls for this comparison */
#typography = createTypographyControlManager(DEFAULT_TYPOGRAPHY_CONTROLS_DATA, 'glyphdiff:comparison:typography'); // #typography = createTypographyControlManager(DEFAULT_TYPOGRAPHY_CONTROLS_DATA, 'glyphdiff:comparison:typography');
/** TanStack Query-backed batch font fetcher */ /** TanStack Query-backed batch font fetcher */
#batchStore: BatchFontStore; #batchStore: BatchFontStore;
@@ -99,7 +96,7 @@ export class ComparisonStore {
$effect(() => { $effect(() => {
const fa = this.#fontA; const fa = this.#fontA;
const fb = this.#fontB; const fb = this.#fontB;
const weight = this.#typography.weight; const weight = typographySettingsStore.weight;
if (!fa || !fb) return; if (!fa || !fb) return;
@@ -152,8 +149,8 @@ export class ComparisonStore {
return; return;
} }
const weight = this.#typography.weight; const weight = typographySettingsStore.weight;
const size = this.#typography.renderedSize; const size = typographySettingsStore.renderedSize;
const fontAName = this.#fontA?.name; const fontAName = this.#fontA?.name;
const fontBName = this.#fontB?.name; const fontBName = this.#fontB?.name;
@@ -201,12 +198,12 @@ export class ComparisonStore {
}; };
} }
// ── Getters / Setters ───────────────────────────────────────────────────── // // ── Getters / Setters ─────────────────────────────────────────────────────
/** Typography control manager */ // /** Typography control manager */
get typography() { // get typography() {
return this.#typography; // return typographySettingsStore;
} // }
/** Font for side A */ /** Font for side A */
get fontA() { get fontA() {
@@ -273,7 +270,7 @@ export class ComparisonStore {
this.#fontB = undefined; this.#fontB = undefined;
this.#batchStore.setIds([]); this.#batchStore.setIds([]);
storage.clear(); storage.clear();
this.#typography.reset(); typographySettingsStore.reset();
} }
} }

View File

@@ -3,6 +3,7 @@
Renders a single character with morphing animation Renders a single character with morphing animation
--> -->
<script lang="ts"> <script lang="ts">
import { typographySettingsStore } from '$features/SetupFont';
import { cn } from '$shared/shadcn/utils/shadcn-utils'; import { cn } from '$shared/shadcn/utils/shadcn-utils';
import { comparisonStore } from '../../model'; import { comparisonStore } from '../../model';
@@ -25,7 +26,7 @@ let { char, proximity, isPast }: Props = $props();
const fontA = $derived(comparisonStore.fontA); const fontA = $derived(comparisonStore.fontA);
const fontB = $derived(comparisonStore.fontB); const fontB = $derived(comparisonStore.fontB);
const typography = $derived(comparisonStore.typography); const typography = $derived(typographySettingsStore);
let slot = $state<0 | 1>(0); let slot = $state<0 | 1>(0);
let slotFonts = $state<[string, string]>(['', '']); let slotFonts = $state<[string, string]>(['', '']);

View File

@@ -15,14 +15,13 @@ import {
getContext, getContext,
untrack, untrack,
} from 'svelte'; } from 'svelte';
import { comparisonStore } from '../../model';
import FontList from '../FontList/FontList.svelte'; import FontList from '../FontList/FontList.svelte';
import Header from '../Header/Header.svelte'; import Header from '../Header/Header.svelte';
import Sidebar from '../Sidebar/Sidebar.svelte'; import Sidebar from '../Sidebar/Sidebar.svelte';
import SliderArea from '../SliderArea/SliderArea.svelte'; import SliderArea from '../SliderArea/SliderArea.svelte';
const responsive = getContext<ResponsiveManager>('responsive'); const responsive = getContext<ResponsiveManager>('responsive');
const typography = $derived(comparisonStore.typography); // const typography = $derived(comparisonStore.typography);
const isMobileOrTabletPortrait = $derived(responsive.isMobile || responsive.isTabletPortrait); const isMobileOrTabletPortrait = $derived(responsive.isMobile || responsive.isTabletPortrait);
let isSidebarOpen = $state(!isMobileOrTabletPortrait); let isSidebarOpen = $state(!isMobileOrTabletPortrait);
@@ -45,50 +44,51 @@ $effect(() => {
{#snippet main()} {#snippet main()}
<FontList /> <FontList />
{/snippet} {/snippet}
<!--
{#snippet controls()} {#snippet controls()}
{#if typography.sizeControl && typography.weightControl && typography.heightControl && typography.spacingControl} {#if typography.sizeControl && typography.weightControl && typography.heightControl && typography.spacingControl}
<ControlGroup label="Size"> <ControlGroup label="Size">
<Slider
bind:value={typography.sizeControl.value}
min={typography.sizeControl.min}
max={typography.sizeControl.max}
step={typography.sizeControl.step}
/>
</ControlGroup>
<ControlGroup label="Weight">
<Slider
bind:value={typography.weightControl.value}
min={typography.weightControl.min}
max={typography.weightControl.max}
step={typography.weightControl.step}
/>
</ControlGroup>
<div class="grid grid-cols-2 gap-6 mt-4">
<ControlGroup label="Leading" class="border-0 py-0">
<Slider <Slider
bind:value={typography.heightControl.value} bind:value={typography.sizeControl.value}
min={typography.heightControl.min} min={typography.sizeControl.min}
max={typography.heightControl.max} max={typography.sizeControl.max}
step={typography.heightControl.step} step={typography.sizeControl.step}
format={(v => v.toFixed(1))}
/> />
</ControlGroup> </ControlGroup>
<ControlGroup label="Tracking" class="border-0 py-0"> <ControlGroup label="Weight">
<Slider <Slider
bind:value={typography.spacingControl.value} bind:value={typography.weightControl.value}
min={typography.spacingControl.min} min={typography.weightControl.min}
max={typography.spacingControl.max} max={typography.weightControl.max}
step={typography.spacingControl.step} step={typography.weightControl.step}
format={(v => v.toFixed(2))}
/> />
</ControlGroup> </ControlGroup>
</div>
{/if} <div class="grid grid-cols-2 gap-6 mt-4">
{/snippet} <ControlGroup label="Leading" class="border-0 py-0">
<Slider
bind:value={typography.heightControl.value}
min={typography.heightControl.min}
max={typography.heightControl.max}
step={typography.heightControl.step}
format={(v => v.toFixed(1))}
/>
</ControlGroup>
<ControlGroup label="Tracking" class="border-0 py-0">
<Slider
bind:value={typography.spacingControl.value}
min={typography.spacingControl.min}
max={typography.spacingControl.max}
step={typography.spacingControl.step}
format={(v => v.toFixed(2))}
/>
</ControlGroup>
</div>
{/if}
{/snippet}
-->
</Sidebar> </Sidebar>
{/snippet} {/snippet}
</SidebarContainer> </SidebarContainer>

View File

@@ -8,6 +8,7 @@ import {
FontVirtualList, FontVirtualList,
type UnifiedFont, type UnifiedFont,
} from '$entities/Font'; } from '$entities/Font';
import { typographySettingsStore } from '$features/SetupFont';
import { import {
Button, Button,
Label, Label,
@@ -18,7 +19,7 @@ import { crossfade } from 'svelte/transition';
import { comparisonStore } from '../../model'; import { comparisonStore } from '../../model';
const side = $derived(comparisonStore.side); const side = $derived(comparisonStore.side);
const typography = $derived(comparisonStore.typography); const typography = $derived(typographySettingsStore);
let prevIndexA: number | null = null; let prevIndexA: number | null = null;
let prevIndexB: number | null = null; let prevIndexB: number | null = null;
@@ -95,7 +96,7 @@ $effect(() => {
class="w-full px-3 md:px-4 py-2.5 md:py-3 justify-between text-left text-sm flex" class="w-full px-3 md:px-4 py-2.5 md:py-3 justify-between text-left text-sm flex"
iconPosition="right" iconPosition="right"
> >
<FontApplicator {font} weight={typography.weight}>{font.name}</FontApplicator> <FontApplicator {font}>{font.name}</FontApplicator>
{#snippet icon()} {#snippet icon()}
{#if active} {#if active}

View File

@@ -3,8 +3,8 @@
Renders a line of text in the SliderArea Renders a line of text in the SliderArea
--> -->
<script lang="ts"> <script lang="ts">
import { typographySettingsStore } from '$features/SetupFont';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import { comparisonStore } from '../../model';
interface LineChar { interface LineChar {
char: string; char: string;
@@ -26,7 +26,7 @@ interface Props {
*/ */
character: Snippet<[{ char: string; index: number }]>; character: Snippet<[{ char: string; index: number }]>;
} }
const typography = $derived(comparisonStore.typography); const typography = $derived(typographySettingsStore);
let { chars, character }: Props = $props(); let { chars, character }: Props = $props();
</script> </script>

View File

@@ -8,6 +8,8 @@
- Performance optimized using offscreen canvas for measurements and transform-based animations. - Performance optimized using offscreen canvas for measurements and transform-based animations.
--> -->
<script lang="ts"> <script lang="ts">
import { TypographyMenu } from '$features/SetupFont';
import { typographySettingsStore } from '$features/SetupFont/model';
import { import {
type ResponsiveManager, type ResponsiveManager,
debounce, debounce,
@@ -42,7 +44,7 @@ let { isSidebarOpen = false, class: className }: Props = $props();
const fontA = $derived(comparisonStore.fontA); const fontA = $derived(comparisonStore.fontA);
const fontB = $derived(comparisonStore.fontB); const fontB = $derived(comparisonStore.fontB);
const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady); const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady);
const typography = $derived(comparisonStore.typography); const typography = $derived(typographySettingsStore);
let container = $state<HTMLElement>(); let container = $state<HTMLElement>();
@@ -179,12 +181,7 @@ const scaleClass = $derived(
The paper div inside scales down when the sidebar opens on desktop. The paper div inside scales down when the sidebar opens on desktop.
--> -->
<div class={cn('flex-1 relative flex items-center justify-center p-0 overflow-hidden bg-surface dark:bg-dark-bg', className)}> <div class={cn('flex-1 relative flex items-center justify-center p-0 overflow-hidden bg-surface dark:bg-dark-bg', className)}>
<!-- <!-- Paper surface -->
Paper surface.
Replaces the old glassmorphism card with a clean white/dark sheet.
Scale transition replaces motion.div spring — CSS transition-transform
is smooth enough here; a JS spring would add ~4kb for minimal gain.
-->
<div <div
class={cn( class={cn(
'w-full h-full flex flex-col items-center justify-center relative', 'w-full h-full flex flex-col items-center justify-center relative',
@@ -248,4 +245,10 @@ const scaleClass = $derived(
{/if} {/if}
</div> </div>
</div> </div>
<TypographyMenu
class={cn(
'absolute bottom-4 sm:bottom-5 right-4 sm:left-1/2 sm:right-[unset] sm:-translate-x-1/2 z-50',
)}
/>
</div> </div>

View File

@@ -14,7 +14,7 @@ import {
import { FontSampler } from '$features/DisplayFont'; import { FontSampler } from '$features/DisplayFont';
import { import {
TypographyMenu, TypographyMenu,
controlManager, typographySettingsStore,
} from '$features/SetupFont'; } from '$features/SetupFont';
import { throttle } from '$shared/lib/utils'; import { throttle } from '$shared/lib/utils';
import { Skeleton } from '$shared/ui'; import { Skeleton } from '$shared/ui';
@@ -60,11 +60,11 @@ const checkPosition = throttle(() => {
const fontRowHeight = $derived.by(() => const fontRowHeight = $derived.by(() =>
createFontRowSizeResolver({ createFontRowSizeResolver({
getFonts: () => fontStore.fonts, getFonts: () => fontStore.fonts,
getWeight: () => controlManager.weight, getWeight: () => typographySettingsStore.weight,
getPreviewText: () => text, getPreviewText: () => text,
getContainerWidth: () => containerWidth, getContainerWidth: () => containerWidth,
getFontSizePx: () => controlManager.renderedSize, getFontSizePx: () => typographySettingsStore.renderedSize,
getLineHeightPx: () => controlManager.height * controlManager.renderedSize, getLineHeightPx: () => typographySettingsStore.height * typographySettingsStore.renderedSize,
getStatus: key => appliedFontsManager.statuses.get(key), getStatus: key => appliedFontsManager.statuses.get(key),
contentHorizontalPadding: SAMPLER_CONTENT_PADDING_X, contentHorizontalPadding: SAMPLER_CONTENT_PADDING_X,
chromeHeight: SAMPLER_CHROME_HEIGHT, chromeHeight: SAMPLER_CHROME_HEIGHT,
@@ -97,7 +97,7 @@ const fontRowHeight = $derived.by(() =>
<FontVirtualList <FontVirtualList
itemHeight={fontRowHeight} itemHeight={fontRowHeight}
useWindowScroll={true} useWindowScroll={true}
weight={controlManager.weight} weight={typographySettingsStore.weight}
columns={layoutManager.columns} columns={layoutManager.columns}
gap={layoutManager.gap} gap={layoutManager.gap}
{skeleton} {skeleton}