@flemo/core 1.29.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/core/TaskManger.d.ts +13 -0
  2. package/dist/core/engine/__tests__/architectureDoc.test.d.ts +10 -0
  3. package/dist/core/engine/__tests__/diagnosticRegistry.test.d.ts +10 -0
  4. package/dist/core/engine/__tests__/flightRouting.test.d.ts +1 -0
  5. package/dist/core/engine/__tests__/landingGovernor.test.d.ts +1 -0
  6. package/dist/core/engine/__tests__/layerRiders.test.d.ts +1 -0
  7. package/dist/core/engine/__tests__/participantLayers.test.d.ts +1 -0
  8. package/dist/core/engine/__tests__/singleResolution.test.d.ts +1 -0
  9. package/dist/core/engine/arrivalHold.d.ts +1 -1
  10. package/dist/core/engine/cancelResume.d.ts +17 -0
  11. package/dist/core/engine/compositorWarmUp.d.ts +1 -1
  12. package/dist/core/engine/createSwipeController.d.ts +16 -0
  13. package/dist/core/engine/createTransitionEngine.d.ts +2 -1
  14. package/dist/core/engine/diagnosticFlags.d.ts +2 -10
  15. package/dist/core/engine/diagnosticRegistry.d.ts +38 -0
  16. package/dist/core/engine/flightHolds.d.ts +22 -0
  17. package/dist/core/engine/flightParticipants.d.ts +8 -0
  18. package/dist/core/engine/flightRouting.d.ts +91 -0
  19. package/dist/core/engine/gpuPipelinePrewarm.d.ts +1 -1
  20. package/dist/core/engine/imageDecodeOffloader.d.ts +1 -1
  21. package/dist/core/engine/landingGovernor.d.ts +3 -0
  22. package/dist/core/engine/layerRiders.d.ts +21 -0
  23. package/dist/core/engine/participantLayers.d.ts +12 -0
  24. package/dist/core/engine/types.d.ts +2 -1
  25. package/dist/dom/__tests__/attributes.test.d.ts +10 -0
  26. package/dist/dom/__tests__/stacking.test.d.ts +1 -0
  27. package/dist/dom/attributes.d.ts +239 -0
  28. package/dist/dom/stacking.d.ts +28 -0
  29. package/dist/history/store.d.ts +0 -1
  30. package/dist/index.d.ts +20 -4
  31. package/dist/index.mjs +2880 -1807
  32. package/dist/morph/__tests__/attachMorph.test.d.ts +1 -0
  33. package/dist/morph/__tests__/morphAnimations.test.d.ts +1 -0
  34. package/dist/morph/__tests__/morphClip.test.d.ts +1 -0
  35. package/dist/morph/__tests__/morphGeometry.test.d.ts +1 -0
  36. package/dist/morph/__tests__/morphKeyframes.test.d.ts +1 -0
  37. package/dist/morph/__tests__/morphLayer.test.d.ts +1 -0
  38. package/dist/morph/__tests__/morphPaint.test.d.ts +1 -0
  39. package/dist/morph/__tests__/morphPose.test.d.ts +1 -0
  40. package/dist/morph/__tests__/morphSheet.test.d.ts +1 -0
  41. package/dist/morph/__tests__/morphSide.test.d.ts +1 -0
  42. package/dist/morph/__tests__/morphSwipe.test.d.ts +1 -0
  43. package/dist/morph/attachMorph.d.ts +59 -0
  44. package/dist/morph/morphAnimations.d.ts +2 -0
  45. package/dist/morph/morphClip.d.ts +31 -0
  46. package/dist/morph/morphGeometry.d.ts +111 -0
  47. package/dist/morph/morphKeyframes.d.ts +190 -0
  48. package/dist/morph/morphLayer.d.ts +14 -0
  49. package/dist/morph/morphPaint.d.ts +31 -0
  50. package/dist/morph/morphPose.d.ts +44 -0
  51. package/dist/morph/morphSheet.d.ts +9 -0
  52. package/dist/morph/morphSide.d.ts +43 -0
  53. package/dist/morph/morphSwipe.d.ts +24 -0
  54. package/dist/navigate/createNavigationController.d.ts +0 -1
  55. package/dist/navigate/store.d.ts +1 -0
  56. package/dist/platform/__tests__/displayCadence.test.d.ts +1 -0
  57. package/dist/platform/__tests__/displayProbe.test.d.ts +1 -0
  58. package/dist/platform/__tests__/engineProbes.test.d.ts +1 -0
  59. package/dist/platform/__tests__/legacyBlinkGate.test.d.ts +1 -0
  60. package/dist/platform/__tests__/profile.test.d.ts +1 -0
  61. package/dist/platform/__tests__/steadySixtyCadence.test.d.ts +1 -0
  62. package/dist/platform/displayCadence.d.ts +3 -0
  63. package/dist/platform/displayProbe.d.ts +5 -0
  64. package/dist/platform/engineProbes.d.ts +4 -0
  65. package/dist/platform/profile.d.ts +70 -0
  66. package/dist/{core/engine → platform}/steadySixtyCadence.d.ts +1 -1
  67. package/dist/runtime/__tests__/flemoRuntime.test.d.ts +1 -0
  68. package/dist/runtime/flemoRuntime.d.ts +9 -0
  69. package/dist/transition/compileTransitionStyles.d.ts +0 -1
  70. package/dist/transition/cubicBezier.d.ts +25 -0
  71. package/dist/transition/morphTransition/__tests__/createMorphTransition.test.d.ts +1 -0
  72. package/dist/transition/morphTransition/createMorphTransition.d.ts +13 -0
  73. package/dist/transition/morphTransition/createRawMorphTransition.d.ts +17 -0
  74. package/dist/transition/morphTransition/morphTransition.d.ts +2 -0
  75. package/dist/transition/morphTransition/shared.d.ts +2 -0
  76. package/dist/transition/morphTransition/text.d.ts +2 -0
  77. package/dist/transition/morphTransition/typing.d.ts +72 -0
  78. package/dist/transition/morphTransition/zoom.d.ts +2 -0
  79. package/dist/transition/registerTransitionDefinitions.d.ts +2 -1
  80. package/dist/transition/swipeSettle.d.ts +82 -1
  81. package/dist/transition/typing.d.ts +2 -2
  82. package/package.json +7 -7
  83. package/dist/core/engine/driverPolicy.d.ts +0 -12
  84. package/dist/core/engine/landingPixelSnap.d.ts +0 -10
  85. package/dist/core/engine/motionDriverKind.d.ts +0 -7
  86. package/dist/core/engine/transitionPlayer.d.ts +0 -40
  87. /package/dist/core/engine/__tests__/{driverPolicy.test.d.ts → createSwipeController.dragHooks.test.d.ts} +0 -0
  88. /package/dist/core/engine/__tests__/{landingPixelSnap.test.d.ts → createSwipeController.layerRelease.test.d.ts} +0 -0
  89. /package/dist/core/engine/__tests__/{motionDriverKind.test.d.ts → createSwipeController.stuckGesture.test.d.ts} +0 -0
  90. /package/dist/core/engine/__tests__/{steadySixtyCadence.test.d.ts → createSwipeController.velocity.test.d.ts} +0 -0
  91. /package/dist/core/engine/__tests__/{transitionPlayer.test.d.ts → flightParticipants.test.d.ts} +0 -0
  92. /package/dist/{core/engine → platform}/governedCompiled.d.ts +0 -0
@@ -0,0 +1,70 @@
1
+ export interface PlatformProfile {
2
+ /**
3
+ * This engine presents its compiled screen animations FROM THE MAIN THREAD
4
+ * (WebKit) rather than off the compositor (Blink). Everything about
5
+ * protecting a flight's opening follows from this one fact: where the clock
6
+ * is stamped on the main thread, a heavy commit between the stamp and the
7
+ * first paint is aged away rather than ridden through.
8
+ */
9
+ readonly mainThreadPresented: boolean;
10
+ /**
11
+ * Flip the hold attribute straight onto the DOM inside the readiness rAF
12
+ * instead of routing the release through a state commit. A rAF callback and
13
+ * its own frame's rendering update are atomic, so clock-start and first paint
14
+ * become simultaneous by construction.
15
+ *
16
+ * Device-verified on three populations; see readDesktopReleaseFlipFlag. An
17
+ * authored `driver: "native"` transition takes it too — pass
18
+ * `authoredNativeDriver` to fold that in.
19
+ */
20
+ readonly atomicReleaseFlip: boolean;
21
+ /**
22
+ * Hand the release's reconcile to the NEXT frame rather than flushing it
23
+ * synchronously, so it stops competing with the flight's first present.
24
+ * Only meaningful where the flip already released the hold — without the
25
+ * flip, the state commit IS the release.
26
+ */
27
+ readonly deferReleaseCommit: boolean;
28
+ /**
29
+ * Hold the release until the entering screen's mount render quiesces, so a
30
+ * heavy screen's own commit storm cannot eat the opening frames.
31
+ */
32
+ readonly renderSettleGate: boolean;
33
+ /**
34
+ * Park a push's entering screen ON TOP at near-zero opacity (so the browser
35
+ * genuinely paints its tiles during the hold) rather than beneath its cover.
36
+ */
37
+ readonly parkOver: boolean;
38
+ /**
39
+ * Keep the screen scope's layer promoted at REST. Off everywhere by default:
40
+ * a promotion is also a stacking context, and at rest it outranks any
41
+ * consumer overlay inside the screen.
42
+ */
43
+ readonly restLayerPromotion: boolean;
44
+ /**
45
+ * Rewrite oversized `<img>` sources to decoded-to-scale blobs off the main
46
+ * thread. Auto on legacy Android Blink only — it touches consumer content,
47
+ * so it must never run where the paint is already cheap.
48
+ */
49
+ readonly imageDecodeOffload: boolean;
50
+ }
51
+ export interface PlatformProfileInput {
52
+ /**
53
+ * The transition being flown authored `driver: "native"` — an explicit opt-in
54
+ * to clock surgery, which carries the atomic release flip with it. The
55
+ * binding knows this and core does not, so it is the one input the profile
56
+ * takes.
57
+ */
58
+ readonly authoredNativeDriver?: boolean;
59
+ }
60
+ export declare const resolvePlatformProfile: (input?: PlatformProfileInput) => PlatformProfile;
61
+ /**
62
+ * The REST-promotion decision on its own, as a module-stable function.
63
+ *
64
+ * A binding that server-renders must read this through a hydration-scoped
65
+ * snapshot (it reaches the DOM as an inline style, so a render-phase read
66
+ * mismatches the server HTML), and React's `useSyncExternalStore` requires the
67
+ * reader identity to be stable across renders — which an inline
68
+ * `() => resolvePlatformProfile().restLayerPromotion` would not be.
69
+ */
70
+ export declare const restLayerPromotionEnabled: () => boolean;
@@ -1,5 +1,5 @@
1
1
  export declare const HIGH_REFRESH_MAX_INTERVAL_MS = 12;
2
2
  export declare const reportInFlightCadence: (rawMedianMs: number, rawMaxMs?: number) => void;
3
3
  export declare const steadySixtyVerified: () => boolean;
4
- export declare const steadySixtyPlayerEligible: () => boolean;
4
+ export declare const steadySixtyDesktopProfile: () => boolean;
5
5
  export declare const resetSteadySixtyForTests: () => void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Start flemo's ambient machinery, and return the release for this holder.
3
+ *
4
+ * A binding calls this once per Router mount and releases on unmount. Repeat
5
+ * calls share one runtime; the last release tears it down. Safe on the server:
6
+ * every piece degrades to a no-op without a document.
7
+ */
8
+ export declare const startFlemoRuntime: () => (() => void);
9
+ export declare const resetFlemoRuntimeForTests: () => void;
@@ -9,7 +9,6 @@ export type CssDecl = {
9
9
  export declare const collectAnimatedProperties: (transition: Pick<Transition, "initial" | "variants">) => string[];
10
10
  export declare const targetToDecls: (target: TransitionVariantValue["value"] | InitialTarget) => CssDecl[];
11
11
  export declare const easingToCss: (ease: AnimationOptions["ease"] | undefined) => string;
12
- export declare const softenFrontLoadedEasing: (easingCss: string, durationS: number) => string | null;
13
12
  export declare const animationName: (scope: "screen" | "decorator" | "part", name: string, variant: TransitionVariant) => string;
14
13
  export declare const HEAD_ANIMATION_SUFFIXES: readonly ["-gov", "-deskhead", "-govcreep"];
15
14
  export declare const matchesFlightAnimationName: (eventName: string, expectedName: string) => boolean;
@@ -1,4 +1,29 @@
1
1
  import { AnimationOptions } from './cssTypes';
2
2
  export type EasingFunction = (progress: number) => number;
3
3
  export declare const cubicBezier: (x1: number, y1: number, x2: number, y2: number) => EasingFunction;
4
+ /**
5
+ * The INVERSE of a CSS timing function: given the value progress you want to
6
+ * see, the time progress that produces it.
7
+ *
8
+ * A scrubbed animation needs this. Setting an animation's clock to the drag's
9
+ * fraction moves the CLOCK linearly, and what the eye follows is the curve's
10
+ * output — with an ease that opens slowly, a finger a tenth of the way across
11
+ * moves the element a fiftieth, and everything it did not do is left for the
12
+ * release to rush through. Inverting the curve makes the element track the
13
+ * finger and leaves the release only what is really left.
14
+ *
15
+ * Binary search on y, the same shape as `cubicBezier`'s search on x. An
16
+ * overshooting curve (`backOut`, `anticipate`) is not monotonic in y, so the
17
+ * search returns the first crossing — which is the one before the overshoot,
18
+ * and the only one a drag can mean.
19
+ */
20
+ export declare const invertEasing: (ease: AnimationOptions["ease"] | undefined) => EasingFunction;
21
+ /**
22
+ * The control points behind an authored ease, named or spelled out — `null`
23
+ * for `linear`, which has no handles to work with.
24
+ *
25
+ * Exported for the swipe release, which re-aims an authored curve's opening
26
+ * slope onto the gesture that produced it (see swipeSettle.ts).
27
+ */
28
+ export declare const easeControlPoints: (ease: AnimationOptions["ease"] | undefined) => [number, number, number, number] | null;
4
29
  export declare const resolveEasing: (ease: AnimationOptions["ease"] | undefined) => EasingFunction;
@@ -0,0 +1,13 @@
1
+ import { InitialTarget } from '../cssTypes';
2
+ import { TransitionVariantValue } from '../typing';
3
+ import { MorphTransition, MorphTransitionName, MorphTransitionOptions } from './typing';
4
+ interface CreateMorphProps {
5
+ name: MorphTransitionName;
6
+ initial: InitialTarget;
7
+ idle: TransitionVariantValue;
8
+ enter: TransitionVariantValue;
9
+ exit: TransitionVariantValue;
10
+ options?: MorphTransitionOptions;
11
+ }
12
+ export default function createMorphTransition({ name, initial, idle, enter, exit, options }: CreateMorphProps): MorphTransition;
13
+ export {};
@@ -0,0 +1,17 @@
1
+ import { InitialTarget } from '../cssTypes';
2
+ import { TransitionVariantValue } from '../typing';
3
+ import { MorphTransition, MorphTransitionName, MorphTransitionOptions } from './typing';
4
+ interface CreateRawMorphProps {
5
+ name: MorphTransitionName;
6
+ initial: InitialTarget;
7
+ idle: TransitionVariantValue;
8
+ pushOnEnter: TransitionVariantValue;
9
+ pushOnExit: TransitionVariantValue;
10
+ replaceOnEnter: TransitionVariantValue;
11
+ replaceOnExit: TransitionVariantValue;
12
+ popOnEnter: TransitionVariantValue;
13
+ popOnExit: TransitionVariantValue;
14
+ options?: MorphTransitionOptions;
15
+ }
16
+ export default function createRawMorphTransition({ name, initial, idle, pushOnEnter, pushOnExit, replaceOnEnter, replaceOnExit, popOnEnter, popOnExit, options }: CreateRawMorphProps): MorphTransition;
17
+ export {};
@@ -0,0 +1,2 @@
1
+ import { MorphTransition, MorphTransitionName } from './typing';
2
+ export declare const morphTransitionMap: Map<MorphTransitionName, MorphTransition>;
@@ -0,0 +1,2 @@
1
+ declare const shared: import('./typing').MorphTransition;
2
+ export default shared;
@@ -0,0 +1,2 @@
1
+ declare const text: import('./typing').MorphTransition;
2
+ export default text;
@@ -0,0 +1,72 @@
1
+ import { BaseTransition, TransitionVariant } from '../typing';
2
+ export interface RegisterMorphTransition {
3
+ }
4
+ export type MorphTransitionName = RegisterMorphTransition[keyof RegisterMorphTransition] | "shared" | "text" | "zoom" | (string & {});
5
+ /** The preset every morph falls back to when no name is given. */
6
+ export declare const DEFAULT_MORPH_TRANSITION_NAME = "shared";
7
+ export type MorphTransitionOptions = {
8
+ /**
9
+ * The share of the flight over which the GHOST dissolves — the copy of the
10
+ * element being replaced that the runtime carries inside the flight (0–1,
11
+ * default 0.55).
12
+ *
13
+ * It is what makes a travel show the right thing at both ends: the flight
14
+ * begins as an exact copy of what was on glass and dissolves into the real
15
+ * arriving element while the box moves. Set it to 0 to carry no copy, which
16
+ * cuts straight to the arrival's content on the first frame.
17
+ *
18
+ * The arriving element stays opaque underneath throughout. Fading both would
19
+ * bleed the background through the pair by a(1 - a) — a luminance dip
20
+ * peaking at 25% in the middle of the hand-over.
21
+ *
22
+ * THE WINDOW IS WHAT THE DEPARTURE'S OWN CONTENT LEAVES OVER. This used to be
23
+ * 0.22 — three frames of a short flight — on the reasoning that holding the
24
+ * copy longer let the two layouts drift apart underneath it and print as
25
+ * doubled text. That reasoning was written when the copy painted everything,
26
+ * including the paired elements. It does not now: a paired descendant is
27
+ * already invisible in the copy (the real one is morphing underneath it), so
28
+ * all the copy is holding is content with NO counterpart — a caption, a body
29
+ * paragraph, a button that exists on one side only. Three frames is not that
30
+ * content leaving; it is that content being cut, which is exactly what a pop
31
+ * looked like next to the arrival it was supposed to reverse.
32
+ */
33
+ crossFade?: number;
34
+ /**
35
+ * Interpolate `border-radius` across the flight (default true). Nothing is
36
+ * scaled, so the two ends' own values are the whole story.
37
+ *
38
+ * It rides the CONTENT animation, never the geometry one: a keyframe that
39
+ * lists a property the compositor cannot animate drops that whole animation
40
+ * to the main thread, and the geometry keyframe is the one that must never
41
+ * leave the compositor.
42
+ */
43
+ radius?: boolean;
44
+ /**
45
+ * Move the whole screen with the element, not just the element (default off).
46
+ *
47
+ * A plain morph flies ALONE: the element crosses while the screens do
48
+ * whatever their own transition says, which is right when the element is one
49
+ * thing among many. It is wrong when the element IS the navigation — a grid
50
+ * cell opening into a full-screen view. There the eye reads the tap as
51
+ * "the camera moved to this card", and a grid that stays put underneath
52
+ * reads as the card escaping from it.
53
+ *
54
+ * `"screen"` gives the flight a camera: the screen the element is SMALL on
55
+ * (the grid — the departing screen on a push, the arriving one on a pop) is
56
+ * scaled and translated by exactly the zoom that takes the element from one
57
+ * end to the other, so every other card moves as though the viewport had
58
+ * pushed in on the tapped one. Material calls the pattern a container
59
+ * transform; iOS 18 calls its version a zoom transition.
60
+ *
61
+ * It SUPERSEDES that screen's own transform for the length of the flight —
62
+ * the camera IS the screen's motion here, and two authors of one transform
63
+ * is not a thing CSS can compose. Pair it with a transition that does not
64
+ * move the screen itself (`none`, or an opacity-only one); a slide would be
65
+ * replaced rather than combined.
66
+ */
67
+ carry?: "screen";
68
+ };
69
+ export interface MorphTransition extends Omit<BaseTransition, "name">, MorphTransitionOptions {
70
+ name: MorphTransitionName;
71
+ }
72
+ export declare const MORPH_FROM_VARIANT: Record<TransitionVariant, "initial" | "IDLE-true" | "self">;
@@ -0,0 +1,2 @@
1
+ declare const zoom: import('./typing').MorphTransition;
2
+ export default zoom;
@@ -1,4 +1,5 @@
1
1
  import { Transition } from './typing';
2
2
  import { Decorator } from './decorator/typing';
3
+ import { MorphTransition } from './morphTransition/typing';
3
4
  import { PartTransition } from './partTransition/typing';
4
- export default function registerTransitionDefinitions(transitions: Transition[], decorators: Decorator[], partTransitions?: PartTransition[]): () => void;
5
+ export default function registerTransitionDefinitions(transitions: Transition[], decorators: Decorator[], partTransitions?: PartTransition[], morphTransitions?: MorphTransition[]): () => void;
@@ -1,4 +1,22 @@
1
1
  export declare const MIN_SETTLE_SECONDS = 0.12;
2
+ /**
3
+ * The initial slope a release curve is allowed to reach, as a multiple of its
4
+ * own average speed — the ceiling on "the finger was going fast".
5
+ *
6
+ * It is also what the by-speed length is derived from: a curve that starts at
7
+ * the finger's speed and decelerates to rest covers the remaining distance in
8
+ * `slope x remaining / speed`, not `remaining / speed`. The old formula solved
9
+ * for a motion that never slows down, which no landing does.
10
+ */
11
+ export declare const RELEASE_LAUNCH_SLOPE = 1.6;
12
+ /**
13
+ * The floor on that slope. A very slow drag with most of the screen left
14
+ * cannot be honored literally — matching 150 px/s over 310px would take two
15
+ * seconds — so the settle does accelerate away from it. It leaves at half its
16
+ * average rather than at 2.25x, which is the difference between the system
17
+ * taking over and the screen being snatched.
18
+ */
19
+ export declare const MIN_LAUNCH_SLOPE = 0.5;
2
20
  export declare const MIN_REVERSAL_SECONDS = 0.28;
3
21
  export interface SwipeSettleInput {
4
22
  remainingPx: number;
@@ -6,6 +24,69 @@ export interface SwipeSettleInput {
6
24
  velocityPxPerSecond: number;
7
25
  reversing?: boolean;
8
26
  authoredSeconds: number;
27
+ /**
28
+ * The authored curve's control points. Given them, the distance term is the
29
+ * time that curve itself spends on the stretch that is left; without them it
30
+ * falls back to reading the motion as linear, which is what this used to do
31
+ * for every transition.
32
+ */
33
+ authoredEase?: readonly [number, number, number, number];
9
34
  minSeconds?: number;
10
35
  }
11
- export declare const swipeSettleSeconds: ({ remainingPx, spanPx, velocityPxPerSecond, authoredSeconds, reversing, minSeconds }: SwipeSettleInput) => number;
36
+ /**
37
+ * How long the authored curve takes to travel its LAST `1 - progress` — the
38
+ * stretch a release that let go at `progress` still has to cover.
39
+ *
40
+ * The curve maps time to distance, so this inverts it: find the time at which
41
+ * it has covered `progress`, and return what is left of the duration. Both
42
+ * searches are on a monotone function.
43
+ */
44
+ export declare const authoredTailSeconds: (progress: number, authoredSeconds: number, ease: readonly [number, number, number, number]) => number;
45
+ export declare const swipeSettleSeconds: ({ remainingPx, spanPx, velocityPxPerSecond, authoredSeconds, authoredEase, reversing, minSeconds }: SwipeSettleInput) => number;
46
+ /**
47
+ * The release curve's normalized initial slope: the speed the settle leaves at,
48
+ * as a multiple of its own average. `velocity x seconds / remaining` is exactly
49
+ * the finger's speed expressed in those units.
50
+ *
51
+ * ONE RULE, INCLUDING THE CANCEL. A reversal's finger was going the other way,
52
+ * so the speed it contributes to the settle's own direction is not its own
53
+ * speed — it is zero or less. Zero is what this reads it as, which puts every
54
+ * reversal on the floor: the screen is standing still when the settle begins,
55
+ * and it opens like something standing still.
56
+ *
57
+ * Full velocity continuity for a reversal would mean OVERSHOOT — the screen
58
+ * carrying on the way the finger was pushing for a hair before it returns,
59
+ * which is what a real spring does. That is a different primitive, not a
60
+ * re-aimed bezier, and it is deliberately not attempted here. Clamping at zero
61
+ * is the closest a monotone curve gets.
62
+ */
63
+ export declare const releaseLaunchSlope: ({ remainingPx, velocityPxPerSecond, seconds, authoredSlope, reversing }: {
64
+ remainingPx: number;
65
+ velocityPxPerSecond: number;
66
+ seconds: number;
67
+ /**
68
+ * The opening slope the transition's author drew. The floor never rises
69
+ * above it: a release that the gesture cannot support must not come out
70
+ * HARDER than the authored motion — the floor is there to stop a crawl, not
71
+ * to add energy the author did not ask for. The ceiling is not capped this
72
+ * way; a finger genuinely moving fast should leave fast, even out of a curve
73
+ * drawn to open gently, or the screen reads as braking the moment it is let
74
+ * go.
75
+ */
76
+ authoredSlope?: number;
77
+ reversing?: boolean;
78
+ }) => number | null;
79
+ /**
80
+ * Re-aim an authored cubic-bezier so it LEAVES at `slope` times its average
81
+ * speed, keeping its x handles and its landing.
82
+ *
83
+ * Only `y1` moves. `x1`/`x2` are the authored ones, so the result is still a
84
+ * valid CSS timing function by construction (x monotonicity is a property of
85
+ * the x handles alone) and still lands the way the transition's author drew it
86
+ * — the release differs from the authored curve exactly where the gesture
87
+ * differs from a standing start, and nowhere else.
88
+ *
89
+ * `y1` is capped below 1 so the curve cannot overshoot its own target; where a
90
+ * steep slope would need more, `x1` shrinks to buy it instead.
91
+ */
92
+ export declare const reaimReleaseEase: (authored: readonly [number, number, number, number], slope: number) => [number, number, number, number];
@@ -30,7 +30,7 @@ export interface SwipeInfo {
30
30
  export type SwipeAnimate = (target: HTMLElement, value: TransitionTarget, options?: AnimationOptions) => Promise<void>;
31
31
  export type TransitionOptions = {
32
32
  decoratorName?: DecoratorName;
33
- driver?: "native" | "player";
33
+ driver?: "native";
34
34
  swipeDirection: "x" | "y";
35
35
  onSwipeStart: (event: PointerEvent, info: SwipeInfo, options: {
36
36
  animate: SwipeAnimate;
@@ -52,7 +52,7 @@ export type TransitionOptions = {
52
52
  }) => Promise<boolean>;
53
53
  } | {
54
54
  decoratorName?: DecoratorName;
55
- driver?: "native" | "player";
55
+ driver?: "native";
56
56
  swipeDirection?: never;
57
57
  };
58
58
  export interface BaseTransition {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flemo/core",
3
- "version": "1.29.0",
3
+ "version": "2.0.0",
4
4
  "description": "Framework-agnostic primitives for flemo: history, navigation, transitions, task manager.",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -33,20 +33,20 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
35
  "path-to-regexp": "^8.4.2",
36
- "zustand": "^5.0.14"
36
+ "zustand": "^5.0.15"
37
37
  },
38
38
  "devDependencies": {
39
+ "@flemo/eslint-config": "0.0.0",
40
+ "@flemo/tsconfig": "0.0.0",
39
41
  "@types/node": "^24.13.1",
40
- "@vitest/coverage-v8": "^4.1.10",
42
+ "@vitest/coverage-v8": "^4.1.11",
41
43
  "csstype": "^3.2.3",
42
44
  "eslint": "^9.39.5",
43
45
  "jsdom": "^29.1.1",
44
46
  "typescript": "^6.0.3",
45
- "vite": "^8.2.1",
47
+ "vite": "^8.2.2",
46
48
  "vite-plugin-dts": "^5.0.3",
47
- "vitest": "^4.1.10",
48
- "@flemo/tsconfig": "0.0.0",
49
- "@flemo/eslint-config": "0.0.0"
49
+ "vitest": "^4.1.11"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"
@@ -1,12 +0,0 @@
1
- export declare const FORCE_PIN_TTL_MS: number;
2
- export interface DriverPolicy {
3
- playerAllowed: () => boolean;
4
- pinnedDriver: () => "css" | "raf" | null;
5
- }
6
- export declare const detectBlinkEngine: () => boolean;
7
- export declare const isLegacyAndroidBlink: () => boolean;
8
- export declare const isDesktopMacWebKit: () => boolean;
9
- export declare const isDesktopBlink: () => boolean;
10
- export declare const createDriverPolicy: (playerByDefault?: boolean) => DriverPolicy;
11
- declare const driverPolicy: DriverPolicy;
12
- export default driverPolicy;
@@ -1,10 +0,0 @@
1
- import { VariantMotion } from '../../transition/variantMotion';
2
- import { PerceptualBox } from './perceptualSpan';
3
- export declare const SNAP_VELOCITY_DEVICE_PX_PER_FRAME = 40;
4
- export declare const SNAP_BAND_MAX_DEVICE_PX = 480;
5
- export declare const snappedEasingForMotion: (motion: VariantMotion, box: PerceptualBox, devicePixelRatio: number) => string | null;
6
- export declare const governedEasingForMotion: (motion: VariantMotion, box: PerceptualBox, devicePixelRatio: number, frameIntervalMs: number) => string | null;
7
- export declare const governedBezierForMotion: (motion: VariantMotion | null, box: PerceptualBox, devicePixelRatio: number, frameIntervalMs: number) => {
8
- easing: string;
9
- durationMs: number;
10
- } | null;
@@ -1,7 +0,0 @@
1
- import { Transition } from '../../transition/typing';
2
- import { VariantMotion } from '../../transition/variantMotion';
3
- import { PerceptualBox } from './perceptualSpan';
4
- export declare const NATIVE_PEAK_CSS_PX_PER_FRAME = 6;
5
- export declare const peakTranslationPxPerFrame: (motion: VariantMotion, box: PerceptualBox) => number | null;
6
- export type MotionDriverKind = "native" | "player";
7
- export declare const classifyTransitionDriver: (transition: Transition, _status: string, _box: PerceptualBox) => MotionDriverKind;
@@ -1,40 +0,0 @@
1
- import { VariantMotion } from '../../transition/variantMotion';
2
- export { resetSessionOverrideCachesForTests } from './diagnosticFlags';
3
- export declare const isPlayerDrivable: (motion: VariantMotion) => boolean;
4
- export interface SnapMemory {
5
- x: number | null;
6
- y: number | null;
7
- landingX?: number | null;
8
- landingY?: number | null;
9
- writtenX?: number | null;
10
- writtenY?: number | null;
11
- pendingLanding?: boolean;
12
- glide?: boolean;
13
- }
14
- export declare const learnedFrameIntervalMs: () => number;
15
- export declare const reportDisplayIntervalMs: (intervalMs: number) => void;
16
- export interface PlayerScheduler {
17
- request: (callback: (time: number) => void) => number;
18
- cancel: (handle: number) => void;
19
- devicePixelRatio: () => number;
20
- }
21
- export interface TrackInput {
22
- element: HTMLElement;
23
- motion: VariantMotion;
24
- role: "active" | "passive";
25
- onComplete?: () => void;
26
- status?: string;
27
- }
28
- export interface TransitionPlayerRegistry {
29
- join: (taskId: string, input: TrackInput) => (() => void) | null;
30
- dispose: (taskId: string) => void;
31
- onFrameGap?: (gapMs: number) => void;
32
- }
33
- export declare const createTransitionPlayerRegistry: (scheduler?: PlayerScheduler) => TransitionPlayerRegistry;
34
- declare const transitionPlayers: TransitionPlayerRegistry;
35
- export default transitionPlayers;
36
- declare global {
37
- interface Window {
38
- __flemoPlayerGaps?: number[];
39
- }
40
- }