From e0b19094758044b6e6b9e57d8a13e19d738c6952 Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Wed, 29 Jul 2026 18:37:10 -0700 Subject: [PATCH] refactor(studio): split ease mode controls out of EaseCurveSection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EaseCurveSection.tsx was 635 lines against CI's 600-line cap, which has been failing the File size check on main for four consecutive runs and blocks release cuts. Moves the two self-contained presentational pieces into a sibling EaseModeControls.tsx, following the pattern the directory already uses (easeCurveSvg, easePresetLibrary, EaseParamFields): the mode radio group (EaseModeToggle) and the preset grid (EasePresetGrid), plus the mode vocabulary they own — EASE_MODES, the EaseMode type, MODE_LABELS, DEFAULT_EASE_BY_MODE, and the DEFAULT_CURVE/Pts pair those defaults are built from. Both components are stateless: they take the current selection and emit a committed ease string, so nothing had to be rewired. Only the symbols the parent still references are exported — EASE_MODES and DEFAULT_EASE_BY_MODE became file-internal, since the components that consume them moved too. No behaviour change. EaseCurveSection is now 556 lines, the new file 110. Co-Authored-By: Claude Opus 5 (1M context) --- .../components/editor/EaseCurveSection.tsx | 99 ++-------------- .../components/editor/EaseModeControls.tsx | 110 ++++++++++++++++++ 2 files changed, 119 insertions(+), 90 deletions(-) create mode 100644 packages/studio/src/components/editor/EaseModeControls.tsx diff --git a/packages/studio/src/components/editor/EaseCurveSection.tsx b/packages/studio/src/components/editor/EaseCurveSection.tsx index f2f6ce030..bc85b11cf 100644 --- a/packages/studio/src/components/editor/EaseCurveSection.tsx +++ b/packages/studio/src/components/editor/EaseCurveSection.tsx @@ -5,7 +5,15 @@ import { parseWiggleEase, type WiggleEaseConfig, } from "@hyperframes/core/wiggle-ease"; -import { EASE_PRESETS, easePresetLabel } from "./easePresetLibrary"; +import { easePresetLabel } from "./easePresetLibrary"; +import { + DEFAULT_CURVE, + MODE_LABELS, + EaseModeToggle, + EasePresetGrid, + type EaseMode, + type Pts, +} from "./EaseModeControls"; import { holdCurvePath, MiniCurveSvg, sampledPath } from "./easeCurveSvg"; import { EaseBezierField, SpringBounceField, WiggleField } from "./EaseParamFields"; import { EASE_CURVES, EASE_LABELS, resolveEaseCurveTuple } from "./gsapAnimationConstants"; @@ -14,51 +22,6 @@ import type { AnimationKeyframeTarget } from "../../hooks/gsapTweenSynth"; export { MiniCurveSvg } from "./easeCurveSvg"; -const EASE_MODES = ["curve", "spring", "wiggle"] as const; -type EaseMode = (typeof EASE_MODES)[number]; - -const EasePresetGrid = function EasePresetGrid({ - kind, - currentEase, - onSelect, -}: { - kind: EaseMode; - currentEase: string; - onSelect: (ease: string) => void; -}) { - return ( -
- {EASE_PRESETS.filter((preset) => preset.kind === kind).map((preset) => { - const isActive = currentEase === preset.ease; - return ( - - ); - })} -
- ); -}; - const round2 = roundToCenti; // ── Graph geometry (Figma-style easing box) ───────────────────────────────── @@ -81,50 +44,6 @@ const DRAG_VMAX = 2; const DRAG_VMIN = -1; const ACCENT = "#3CE6AC"; -type Pts = [number, number, number, number]; -const DEFAULT_CURVE: Pts = EASE_CURVES["power2.out"]; -const MODE_LABELS = { curve: "Curve", spring: "Spring", wiggle: "Wiggle" } satisfies Record< - EaseMode, - string ->; -const DEFAULT_EASE_BY_MODE = { - curve: `custom(M0,0 C${DEFAULT_CURVE[0]},${DEFAULT_CURVE[1]} ${DEFAULT_CURVE[2]},${DEFAULT_CURVE[3]} 1,1)`, - spring: "spring(0.42)", - wiggle: "wiggle(3,easeInOut,0.12)", -} satisfies Record; - -function EaseModeToggle({ mode, onCommit }: { mode: EaseMode; onCommit: (ease: string) => void }) { - return ( -
- {EASE_MODES.map((candidateMode) => { - const active = candidateMode === mode; - return ( - - ); - })} -
- ); -} - // Figma-style ease-type dropdown: the current ease (glyph + name) as a button // that opens the preset grid in a popover. This is where a preset is selected — // the grid is no longer shown inline. diff --git a/packages/studio/src/components/editor/EaseModeControls.tsx b/packages/studio/src/components/editor/EaseModeControls.tsx new file mode 100644 index 000000000..40cac0ab3 --- /dev/null +++ b/packages/studio/src/components/editor/EaseModeControls.tsx @@ -0,0 +1,110 @@ +/** + * Ease editor mode primitives: the mode radio group and the preset grid. + * + * Split out of `EaseCurveSection.tsx` to keep it under the 600-line cap + * (CI's file size check). Both components are presentational and stateless — + * they take the current selection and emit a committed ease string — so they + * carry the mode vocabulary (`EASE_MODES`, labels, per-mode defaults) with + * them rather than importing it back from the parent. + */ + +import { EASE_PRESETS } from "./easePresetLibrary"; +import { MiniCurveSvg } from "./easeCurveSvg"; +import { EASE_CURVES } from "./gsapAnimationConstants"; + +const EASE_MODES = ["curve", "spring", "wiggle"] as const; +export type EaseMode = (typeof EASE_MODES)[number]; + +export type Pts = [number, number, number, number]; +export const DEFAULT_CURVE: Pts = EASE_CURVES["power2.out"]; + +export const MODE_LABELS = { curve: "Curve", spring: "Spring", wiggle: "Wiggle" } satisfies Record< + EaseMode, + string +>; + +const DEFAULT_EASE_BY_MODE = { + curve: `custom(M0,0 C${DEFAULT_CURVE[0]},${DEFAULT_CURVE[1]} ${DEFAULT_CURVE[2]},${DEFAULT_CURVE[3]} 1,1)`, + spring: "spring(0.42)", + wiggle: "wiggle(3,easeInOut,0.12)", +} satisfies Record; + +export const EasePresetGrid = function EasePresetGrid({ + kind, + currentEase, + onSelect, +}: { + kind: EaseMode; + currentEase: string; + onSelect: (ease: string) => void; +}) { + return ( +
+ {EASE_PRESETS.filter((preset) => preset.kind === kind).map((preset) => { + const isActive = currentEase === preset.ease; + return ( + + ); + })} +
+ ); +}; + +export function EaseModeToggle({ + mode, + onCommit, +}: { + mode: EaseMode; + onCommit: (ease: string) => void; +}) { + return ( +
+ {EASE_MODES.map((candidateMode) => { + const active = candidateMode === mode; + return ( + + ); + })} +
+ ); +}