feat(studio): add stateless snap engine with alignment computation (#1227)

Pure-function snap computation module with zero React/DOM dependencies:

- resolveSnapAdjustment: edge/center alignment for drag gestures
- resolveResizeSnapAdjustment: snap only active resize edges
- resolveEquidistanceGuides: Figma-style spacing indicators
- extractSnapTargets, buildCompositionSnapTarget, buildGridSnapEdges
- studioUiPreferences: snap/grid settings persistence
- 38 unit tests covering threshold, grid priority, stress scenarios
This commit is contained in:
Miguel Ángel
2026-06-05 17:54:19 -04:00
committed by GitHub
parent 1324de54a8
commit 1863831a4d
3 changed files with 1249 additions and 0 deletions
@@ -11,6 +11,10 @@ export interface StudioUiPreferences {
audioMuted?: boolean;
previewZoom?: StoredPreviewZoomState;
recentBlocks?: string[];
snapEnabled?: boolean;
gridVisible?: boolean;
gridSpacing?: number;
snapToGrid?: boolean;
}
const STUDIO_UI_PREFERENCES_KEY = "hf-studio-ui-preferences";
@@ -28,6 +32,7 @@ function getBrowserStorage(): Storage | null {
}
}
// fallow-ignore-next-line complexity
function readStorage(storage: Storage | null): StudioUiPreferences {
if (!storage) return {};
try {
@@ -67,6 +72,18 @@ function readStorage(storage: Storage | null): StudioUiPreferences {
(v: unknown): v is string => typeof v === "string",
);
}
if (typeof parsed.snapEnabled === "boolean") {
preferences.snapEnabled = parsed.snapEnabled;
}
if (typeof parsed.gridVisible === "boolean") {
preferences.gridVisible = parsed.gridVisible;
}
if (typeof parsed.gridSpacing === "number" && Number.isFinite(parsed.gridSpacing)) {
preferences.gridSpacing = parsed.gridSpacing;
}
if (typeof parsed.snapToGrid === "boolean") {
preferences.snapToGrid = parsed.snapToGrid;
}
return preferences;
} catch {
return {};