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
93 lines
2.9 KiB
TypeScript
93 lines
2.9 KiB
TypeScript
import { computed, inject, type ComputedRef, type InjectionKey } from "vue";
|
|
import type { OriginAction } from "../actions/types";
|
|
import type {
|
|
OriginContext,
|
|
OriginOperationHandle,
|
|
OriginScene,
|
|
} from "../scene/types";
|
|
import type { OriginView } from "../view/types";
|
|
|
|
/**
|
|
* Injection payload provided by each stable scene-node host.
|
|
*
|
|
* @internal
|
|
*/
|
|
export interface OriginNodeScope {
|
|
/** Scene containing the node. */
|
|
readonly scene: OriginScene;
|
|
/** Unique key of the mounted node. */
|
|
readonly nodeKey: string;
|
|
}
|
|
|
|
/** Node-scoped controls returned by {@link useOrigin}. */
|
|
export interface UseOrigin {
|
|
/** Key of the mounted node containing the calling component. */
|
|
readonly nodeKey: string;
|
|
/** Scene containing the calling component. */
|
|
readonly scene: OriginScene;
|
|
/** Reactive context for the calling component's scene node. */
|
|
readonly context: ComputedRef<OriginContext>;
|
|
/** Reactive shorthand for `context.value.view`. */
|
|
readonly view: ComputedRef<OriginView>;
|
|
/** Reactive shorthand for `context.value.previous`. */
|
|
readonly previous: ComputedRef<OriginView | undefined>;
|
|
/** Reactive shorthand for `context.value.canGoBack`. */
|
|
readonly canGoBack: ComputedRef<boolean>;
|
|
/** Begin an interactively controlled operation from this component. */
|
|
begin(action: OriginAction): Promise<OriginOperationHandle>;
|
|
/** Programmatically create and commit a target from this component. */
|
|
perform(action: OriginAction): Promise<boolean>;
|
|
}
|
|
|
|
/**
|
|
* Injection key used by the internal scene-node host to establish origin
|
|
* ownership for descendant components.
|
|
*
|
|
* Application code normally calls {@link useOrigin} instead of injecting this
|
|
* key directly.
|
|
*
|
|
* @internal
|
|
*/
|
|
export const originNodeScopeKey: InjectionKey<OriginNodeScope> =
|
|
Symbol("origin-node-scope");
|
|
|
|
/**
|
|
* Access the scene from the component that owns an interaction declaration.
|
|
*
|
|
* There is deliberately no `activeView`: the injected node is the origin
|
|
* because this component is where the event or application action occurred.
|
|
*
|
|
* @returns Node-scoped scene state and operation controls.
|
|
* @throws If called outside a component rendered by `OriginScene`.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const origin = useOrigin();
|
|
*
|
|
* function openProfile() {
|
|
* return origin.perform(
|
|
* forward(originView(ProfileView), slideLeft),
|
|
* );
|
|
* }
|
|
* ```
|
|
*/
|
|
export function useOrigin(): UseOrigin {
|
|
const scope = inject(originNodeScopeKey);
|
|
if (!scope)
|
|
throw new Error("useOrigin() must be called inside an <OriginScene> view.");
|
|
|
|
const { scene, nodeKey } = scope;
|
|
const context = computed(() => scene.contextFor(nodeKey));
|
|
|
|
return {
|
|
nodeKey,
|
|
scene,
|
|
context,
|
|
view: computed(() => context.value.view),
|
|
previous: computed(() => context.value.previous),
|
|
canGoBack: computed(() => context.value.canGoBack),
|
|
begin: (action) => scene.begin(nodeKey, action),
|
|
perform: (action) => scene.perform(nodeKey, action),
|
|
};
|
|
}
|