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.
This commit is contained in:
Ilia Mashkov
2026-05-24 22:07:44 +03:00
parent 0c59262a59
commit 06b6274e66
5 changed files with 50 additions and 8 deletions
@@ -4,6 +4,20 @@ import {
prepareWithSegments,
} from '@chenglou/pretext';
/**
* Width of the character morph "halo" around the slider thumb, in percent
* of container width. Characters within this window get partial blending
* instead of a hard A→B flip.
*/
const CHAR_PROXIMITY_RANGE_PCT = 5;
/**
* Default render size in px when callers omit the `size` arg on `layout()`.
* Kept as a local constant to avoid pulling `$entities/Font` into
* `$shared/lib` (would create an FSD-illegal upward import cycle).
*/
const DEFAULT_RENDER_SIZE_PX = 16;
/**
* A single laid-out line produced by dual-font comparison layout.
*
@@ -129,7 +143,7 @@ export class CharacterComparisonEngine {
width: number,
lineHeight: number,
spacing: number = 0,
size: number = 16,
size: number = DEFAULT_RENDER_SIZE_PX,
): ComparisonResult {
if (!text) {
return { lines: [], totalHeight: 0 };
@@ -260,7 +274,7 @@ export class CharacterComparisonEngine {
const chars = line.chars;
const n = chars.length;
const sliderX = (sliderPos / 100) * containerWidth;
const range = 5;
const range = CHAR_PROXIMITY_RANGE_PCT;
// Prefix sums of widthA (left chars will be past → use widthA).
// Suffix sums of widthB (right chars will not be past → use widthB).
// This lets us compute, for each char i, what the total line width and
@@ -291,6 +305,7 @@ export class CharacterComparisonEngine {
const totalRendered = chars.reduce((s, c, i) => s + (isPastArr[i] ? c.widthA : c.widthB), 0);
const xOffset = (containerWidth - totalRendered) / 2;
let currentX = xOffset;
return chars.map((char, i) => {
const isPast = isPastArr[i] === 1;
const charWidth = isPast ? char.widthA : char.widthB;
@@ -1,5 +1,11 @@
import { debounce } from '$shared/lib/utils';
/**
* Default debounce delay used when no wait is provided. Picked to feel
* snappy for typing while still coalescing API-bound side effects.
*/
export const DEFAULT_DEBOUNCE_MS = 300;
/**
* Creates reactive state with immediate and debounced values.
*
@@ -23,7 +29,7 @@ import { debounce } from '$shared/lib/utils';
* <p>Searching: {search.debounced}</p>
* ```
*/
export function createDebouncedState<T>(initialValue: T, wait: number = 300) {
export function createDebouncedState<T>(initialValue: T, wait: number = DEFAULT_DEBOUNCE_MS) {
let immediate = $state(initialValue);
let debounced = $state(initialValue);
@@ -34,6 +34,13 @@ import { Spring } from 'svelte/motion';
*/
const PERSPECTIVE_SPRING_CONFIG = { stiffness: 0.2, damping: 0.8 } as const;
/**
* Halfway threshold on the 0→1 spring value. Above flips `isBack`,
* below flips `isFront`. Picking 0.5 means both states flip at the
* exact midpoint of the animation.
*/
const PERSPECTIVE_TOGGLE_THRESHOLD = 0.5;
/**
* Configuration options for perspective effects
*/
@@ -107,7 +114,7 @@ export class PerspectiveManager {
* Content should appear blurred, scaled down, and less interactive
* when this is true. Derived from spring value > 0.5.
*/
isBack = $derived(this.spring.current > 0.5);
isBack = $derived(this.spring.current > PERSPECTIVE_TOGGLE_THRESHOLD);
/**
* Reactive state: true when in front position
@@ -115,7 +122,7 @@ export class PerspectiveManager {
* Content should be fully visible, sharp, and interactive
* when this is true. Derived from spring value < 0.5.
*/
isFront = $derived(this.spring.current < 0.5);
isFront = $derived(this.spring.current < PERSPECTIVE_TOGGLE_THRESHOLD);
/**
* Internal configuration with defaults applied
@@ -4,6 +4,12 @@
* Used to render visible items with absolute positioning based on computed offsets.
*/
/**
* Minimum height delta (in px) required to commit a re-measured row height.
* Sub-pixel diffs are treated as measurement noise to avoid spurious re-flows.
*/
const MEASUREMENT_EPSILON_PX = 0.5;
export interface VirtualItem {
/**
* Index of the item in the data array
@@ -381,8 +387,8 @@ export function createVirtualizer<T>(
if (!isNaN(index)) {
const oldHeight = measuredSizes[index];
// Only update if the height difference is significant (> 0.5px)
if (oldHeight === undefined || Math.abs(oldHeight - height) > 0.5) {
// Only update if the height difference is significant
if (oldHeight === undefined || Math.abs(oldHeight - height) > MEASUREMENT_EPSILON_PX) {
measurementBuffer[index] = height;
}
}