Rewrite the core-v2 internals without changing the public API or runtime behavior. The four large files (types, scene, motion, gesture) become layered modules with one responsibility each: - shared/: geometry and environment helpers - view/: originView recipes - choreography/: effect types, presets, and composeEffects compositing - actions/: forward/back/replace/above/under via one overload resolver - scene/: SceneGraph storage, transitions (history rules), compositor (styles and interactivity), spring (pure stepper + rAF driver), and createOriginScene, which only wires them and owns async timing - composables/: useOrigin and the node-scope injection key - gesture/: builder, definition->policy normalization, start admission, DirectionalTracker, and a GestureRecognizer session state machine replacing fifteen loose closure variables - components/: renderers and gesture hosts, plus their prop types Verification (packages/core-v2/verify): differential fuzzing drives main and this branch with identical random operation sequences and pointer streams and requires identical scene state, styles, contexts, callbacks, and promise results at every step. It caught every planted mutation that applied. Benchmarks are at parity. Also fix a pre-existing test that called the defineProps macro inside a runtime defineComponent, and correct API.md's description of the flick commit threshold to match the runtime. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Faarh2Ts6StJPcz2mRiHiR
88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
import type { MaybeOriginAction, OriginAction } from "../actions/types";
|
|
import type { OriginContext } from "../scene/types";
|
|
import type {
|
|
OriginGestureCompletionPredicate,
|
|
OriginGestureDefinition,
|
|
OriginGestureDirection,
|
|
OriginGestureOptions,
|
|
OriginGestureStart,
|
|
} from "./types";
|
|
|
|
/** Directed movement in CSS pixels required before a gesture captures. */
|
|
export const DEFAULT_THRESHOLD = 8;
|
|
/** Ratio by which directed movement must exceed cross-axis movement. */
|
|
export const DEFAULT_AXIS_DOMINANCE = 1.15;
|
|
|
|
/**
|
|
* The single shape the recognizer executes. Builder definitions and legacy
|
|
* options objects are both normalized into it.
|
|
*/
|
|
export interface RecognizerPolicy {
|
|
readonly direction: OriginGestureDirection;
|
|
readonly start: OriginGestureStart;
|
|
readonly threshold: number;
|
|
readonly axisDominance: number;
|
|
readonly completion?: OriginGestureCompletionPredicate;
|
|
readonly action: (context: OriginContext) => MaybeOriginAction;
|
|
}
|
|
|
|
function isDefinition(
|
|
value: OriginGestureOptions | OriginGestureDefinition,
|
|
): value is OriginGestureDefinition {
|
|
return "kind" in value && value.kind === "origin-gesture-definition";
|
|
}
|
|
|
|
/** Legacy `edge` is measured from the side opposite the movement direction. */
|
|
const LEGACY_START_EDGE = {
|
|
left: "right",
|
|
right: "left",
|
|
up: "bottom",
|
|
down: "top",
|
|
} as const;
|
|
|
|
function fromLegacyOptions(options: OriginGestureOptions): RecognizerPolicy {
|
|
return {
|
|
direction: options.direction,
|
|
start:
|
|
options.edge === undefined
|
|
? { kind: "anywhere" }
|
|
: Object.freeze({
|
|
kind: "edge",
|
|
edge: LEGACY_START_EDGE[options.direction],
|
|
distance: options.edge,
|
|
}),
|
|
threshold: options.threshold ?? DEFAULT_THRESHOLD,
|
|
axisDominance: DEFAULT_AXIS_DOMINANCE,
|
|
action: options.action,
|
|
};
|
|
}
|
|
|
|
function fromDefinition(definition: OriginGestureDefinition): RecognizerPolicy {
|
|
return {
|
|
direction: definition.direction,
|
|
start: definition.start,
|
|
threshold: definition.recognition.threshold ?? DEFAULT_THRESHOLD,
|
|
axisDominance:
|
|
definition.recognition.axisDominance ?? DEFAULT_AXIS_DOMINANCE,
|
|
completion: definition.completion,
|
|
// Pair the resolved navigation intent with the definition's animation.
|
|
action: async (context) => {
|
|
const navigation = await definition.navigation(context);
|
|
if (!navigation) return navigation;
|
|
const action: OriginAction = {
|
|
...navigation,
|
|
choreography: definition.choreography,
|
|
};
|
|
return action;
|
|
},
|
|
};
|
|
}
|
|
|
|
export function recognizerPolicy(
|
|
definition: OriginGestureDefinition | OriginGestureOptions,
|
|
): RecognizerPolicy {
|
|
return isDefinition(definition)
|
|
? fromDefinition(definition)
|
|
: fromLegacyOptions(definition);
|
|
}
|