kuinetic 0.1.3 → 0.2.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 (125) hide show
  1. package/README.md +93 -10
  2. package/dist/esm/advanced/index.mjs +4727 -0
  3. package/dist/esm/chunk-AEQGC2KM.mjs +189 -0
  4. package/dist/esm/chunk-MQSNMYRP.mjs +9332 -0
  5. package/dist/esm/chunk-RVTYN6NZ.mjs +3446 -0
  6. package/dist/esm/chunk-U43BX5O2.mjs +2984 -0
  7. package/dist/esm/core/index.mjs +16 -9
  8. package/dist/esm/effects/index.mjs +19 -4
  9. package/dist/esm/index.mjs +17 -10
  10. package/dist/kuinetic.advanced.js +5070 -0
  11. package/dist/kuinetic.advanced.min.js +749 -0
  12. package/dist/kuinetic.all.js +15218 -5595
  13. package/dist/kuinetic.all.min.js +2 -0
  14. package/dist/kuinetic.css +2900 -360
  15. package/dist/kuinetic.js +15217 -5593
  16. package/dist/kuinetic.min.css +1 -0
  17. package/dist/kuinetic.min.js +7 -0
  18. package/dist/types/3d/angles.d.ts +30 -0
  19. package/dist/types/3d/flattening.d.ts +49 -0
  20. package/dist/types/3d/index.d.ts +36 -0
  21. package/dist/types/3d/model-3d.d.ts +115 -0
  22. package/dist/types/3d/register.d.ts +26 -0
  23. package/dist/types/3d/webgl.d.ts +24 -0
  24. package/dist/types/advanced/audio.d.ts +322 -0
  25. package/dist/types/advanced/base.d.ts +233 -0
  26. package/dist/types/advanced/camera-3d.d.ts +236 -0
  27. package/dist/types/advanced/fluid-cursor.d.ts +149 -0
  28. package/dist/types/advanced/gl-utils.d.ts +52 -0
  29. package/dist/types/advanced/glsl.d.ts +60 -0
  30. package/dist/types/advanced/index.d.ts +23 -0
  31. package/dist/types/advanced/particles.d.ts +108 -0
  32. package/dist/types/advanced/scenes.d.ts +207 -0
  33. package/dist/types/advanced/shaders.d.ts +972 -0
  34. package/dist/types/browser/boot.d.ts +72 -0
  35. package/dist/types/core/activation.d.ts +227 -2
  36. package/dist/types/core/animator.d.ts +446 -11
  37. package/dist/types/core/attrs.d.ts +9 -0
  38. package/dist/types/core/breakpoints.d.ts +260 -0
  39. package/dist/types/core/bundles.d.ts +71 -0
  40. package/dist/types/core/callback.d.ts +64 -0
  41. package/dist/types/core/capabilities.d.ts +47 -0
  42. package/dist/types/core/channels.d.ts +120 -2
  43. package/dist/types/core/cloak-selectors.d.ts +47 -0
  44. package/dist/types/core/compile.d.ts +207 -2
  45. package/dist/types/core/control.d.ts +218 -0
  46. package/dist/types/core/declarations.d.ts +128 -0
  47. package/dist/types/core/derived/aggregate.d.ts +43 -0
  48. package/dist/types/core/derived/book.d.ts +30 -0
  49. package/dist/types/core/derived/diagnostics.d.ts +51 -0
  50. package/dist/types/core/derived/group-gate.d.ts +62 -0
  51. package/dist/types/core/derived/install.d.ts +110 -0
  52. package/dist/types/core/derived/late-matches.d.ts +19 -0
  53. package/dist/types/core/derived/types.d.ts +108 -0
  54. package/dist/types/core/easing.d.ts +57 -0
  55. package/dist/types/core/element-config.d.ts +47 -6
  56. package/dist/types/core/element-size.d.ts +23 -0
  57. package/dist/types/core/event-sources.d.ts +39 -0
  58. package/dist/types/core/flip.d.ts +34 -1
  59. package/dist/types/core/gesture.d.ts +12 -0
  60. package/dist/types/core/host-facts.d.ts +155 -0
  61. package/dist/types/core/index.d.ts +7 -3
  62. package/dist/types/core/owned-styles.d.ts +129 -2
  63. package/dist/types/core/params.d.ts +48 -0
  64. package/dist/types/core/parse.d.ts +28 -1
  65. package/dist/types/core/register-into.d.ts +29 -0
  66. package/dist/types/core/registry.d.ts +10 -3
  67. package/dist/types/core/repeat.d.ts +91 -0
  68. package/dist/types/core/sequence.d.ts +126 -0
  69. package/dist/types/core/stagger-config.d.ts +199 -0
  70. package/dist/types/core/stagger-keys.d.ts +63 -0
  71. package/dist/types/core/stagger.d.ts +187 -6
  72. package/dist/types/core/style-plan.d.ts +21 -16
  73. package/dist/types/core/target.d.ts +128 -0
  74. package/dist/types/core/threshold-reachability.d.ts +45 -0
  75. package/dist/types/core/time-scale.d.ts +9 -0
  76. package/dist/types/core/toggle-actions.d.ts +141 -0
  77. package/dist/types/core/travel.d.ts +59 -0
  78. package/dist/types/core/types.d.ts +705 -13
  79. package/dist/types/core/unquoted-selectors.d.ts +53 -0
  80. package/dist/types/effects/carousel/drag.d.ts +80 -0
  81. package/dist/types/effects/carousel/index.d.ts +149 -0
  82. package/dist/types/effects/catalog/background-media.d.ts +152 -0
  83. package/dist/types/effects/catalog/discrete.d.ts +13 -0
  84. package/dist/types/effects/catalog/interaction-proximity.d.ts +24 -0
  85. package/dist/types/effects/catalog/interaction-reveal.d.ts +248 -0
  86. package/dist/types/effects/catalog/interaction-shared.d.ts +42 -0
  87. package/dist/types/effects/catalog/interaction-states.d.ts +68 -0
  88. package/dist/types/effects/catalog/materials.d.ts +67 -0
  89. package/dist/types/effects/catalog/media-shared.d.ts +3 -2
  90. package/dist/types/effects/catalog/numbers-shared.d.ts +8 -5
  91. package/dist/types/effects/catalog/shared.d.ts +2 -2
  92. package/dist/types/effects/catalog/text-shared.d.ts +92 -10
  93. package/dist/types/effects/catalog/transforms.d.ts +13 -0
  94. package/dist/types/effects/catalog/view-transitions.d.ts +13 -0
  95. package/dist/types/effects/forms/primitives.d.ts +15 -4
  96. package/dist/types/effects/gestures/index.d.ts +53 -0
  97. package/dist/types/effects/index.d.ts +5 -0
  98. package/dist/types/effects/layout/presets.d.ts +35 -0
  99. package/dist/types/effects/motion-path/index.d.ts +35 -0
  100. package/dist/types/effects/scroll-mechanics/params.d.ts +23 -0
  101. package/dist/types/effects/scroll-mechanics/presets.d.ts +51 -0
  102. package/dist/types/effects/scroll-mechanics/section-index.d.ts +47 -0
  103. package/dist/types/effects/shared.d.ts +162 -2
  104. package/dist/types/effects/step-index.d.ts +121 -0
  105. package/dist/types/effects/step-marking.d.ts +41 -33
  106. package/dist/types/effects/svg/icon-parts.d.ts +23 -0
  107. package/dist/types/effects/three-d/flip-parts.d.ts +46 -0
  108. package/dist/types/effects/tween/index.d.ts +57 -0
  109. package/dist/types/effects/tween/properties.d.ts +107 -0
  110. package/dist/types/effects/tween/waypoints.d.ts +106 -0
  111. package/dist/types/showcase/compare.d.ts +4 -0
  112. package/dist/types/showcase/device-frame.d.ts +3 -0
  113. package/dist/types/showcase/hotspots.d.ts +3 -0
  114. package/dist/types/showcase/index.d.ts +30 -0
  115. package/dist/types/showcase/lightbox.d.ts +3 -0
  116. package/dist/types/showcase/media-source.d.ts +13 -0
  117. package/dist/types/showcase/modal-shell.d.ts +22 -0
  118. package/dist/types/showcase/scroll-story.d.ts +4 -0
  119. package/dist/types/showcase/shared.d.ts +47 -0
  120. package/dist/types/showcase/slideshow.d.ts +3 -0
  121. package/dist/types/showcase/slow-mo.d.ts +5 -0
  122. package/package.json +14 -7
  123. package/dist/esm/chunk-5DON3UFQ.mjs +0 -4380
  124. package/dist/esm/chunk-JT4PZL3A.mjs +0 -773
  125. package/dist/esm/chunk-QUVFODSQ.mjs +0 -1278
@@ -0,0 +1,128 @@
1
+ import type { EffectParams, ParamSpec } from './types.js';
2
+ /**
3
+ * The slice of `PrepareContext` `resolveTarget`/`queryScoped` actually need.
4
+ *
5
+ * A real `PrepareContext` satisfies this structurally — it has both fields and more — so every
6
+ * existing call site (the primitives' own `prepare`, which is handed a full `PrepareContext`)
7
+ * keeps compiling unchanged. Declared narrow rather than importing `PrepareContext` itself so
8
+ * `animator.ts`'s `install` — which resolves a retargeted selector at compile-to-install time,
9
+ * before any primitive's `prepare` has run and therefore before a full `PrepareContext` exists —
10
+ * can build the two fields it actually has (`el.ownerDocument`, a warn callback) instead of
11
+ * fabricating the rest of the interface it does not.
12
+ */
13
+ export interface TargetContext {
14
+ doc: Document;
15
+ warn(message: string): void;
16
+ }
17
+ /**
18
+ * `target:`/`scope:` resolution — one convention shared by every place in the library that lets an
19
+ * author name a selector instead of animating the element the attribute sits on.
20
+ *
21
+ * Lives in `core`, not `effects`, because `core/compile.ts` and `core/animator.ts` need it too:
22
+ * `target:` started as a per-primitive parameter six scroll/forms primitives read for themselves
23
+ * (`effects/step-marking.ts`'s `createStepMarker` still owns the step-index-specific half of that),
24
+ * but retargeting an *arbitrary* effect (`fade-up target:h1`) has to be decided at compile/install
25
+ * time, before any primitive's own `prepare` ever runs. `core` must not depend on `effects` — every
26
+ * import in this codebase already runs the other way — so the shared half of the mechanism lives
27
+ * here and `effects/step-marking.ts` imports it back, rather than `core` reaching into `effects`.
28
+ */
29
+ /**
30
+ * Classify a selector as unusable, dangerously broad, or fine.
31
+ *
32
+ * `matches` against the two roots catches every selector that reaches the whole document — `*`,
33
+ * `html`, `body` and compounds like `*, a` with one rule and no bespoke parser — while leaving a
34
+ * deliberately scoped wildcard such as `.spy-nav > *` working, which a syntactic ban on `*` would
35
+ * not. `matches` throws on invalid syntax exactly as `querySelectorAll` does, so the same call
36
+ * still answers the validity question.
37
+ *
38
+ * @complexity O(1) time and space.
39
+ * @overallScore 100
40
+ */
41
+ export declare function selectorBreadth(selector: string, doc: Document): 'invalid' | 'document-wide' | 'ok';
42
+ /**
43
+ * Resolve a `target:` selector once at setup, rejecting both the unusable and the over-broad.
44
+ *
45
+ * Two distinct failures, one warning channel. An invalid selector thrown from inside the shared
46
+ * scheduler's frame callback would skip every other subscriber on that root, not just this one. An
47
+ * over-broad one is worse for being silent: `target:*` is perfectly valid syntax, so a syntax-only
48
+ * check passed it straight through to stamp an attribute onto every element in the document, on
49
+ * every frame.
50
+ *
51
+ * Rejecting rather than silently narrowing is the honest response, because there is no narrower
52
+ * selector that could be meant. A selector reaching `<html>` or `<body>` is not naming a set of
53
+ * steps or a nav link, it is naming the page, and guessing which of its thousands of descendants
54
+ * the author meant would be worse than saying so.
55
+ *
56
+ * @param selector - The authored `target:` value; empty is the no-op default, not an error.
57
+ * @param ctx - Prepare context, for `doc` and the warning channel.
58
+ * @param effect - Effect name, so the warning says which attribute to go and fix.
59
+ * @returns The selector, or `''` when it must be ignored.
60
+ * @complexity O(1) time and space; `matches` walks the selector, not the document.
61
+ * @overallScore 100
62
+ */
63
+ export declare function resolveTarget(selector: string, ctx: TargetContext, effect: string): string;
64
+ /**
65
+ * Which tree a validated `target:` is searched in.
66
+ *
67
+ * `'self'` is the descendant reading — "search inside myself" — and is what `target:` means
68
+ * everywhere the word is used going forward. `'page'` is the whole document, which a handful of
69
+ * resolution sites need because the element they mark is deliberately somewhere else: `scroll-spy`'s
70
+ * nav link lives outside the section that activates it, and `media-scrub`'s frames may sit in a
71
+ * sibling figure.
72
+ *
73
+ * Two values, declared as a list rather than a boolean, so `'parent'` or `'section'` can be added
74
+ * later by extending {@link SCOPE_PARAM}'s `keywords` — a one-line change with no rename anywhere.
75
+ */
76
+ export type TargetScope = 'self' | 'page';
77
+ /**
78
+ * The one declaration of `scope:`, shared by every primitive that declares `target:`.
79
+ *
80
+ * Declared once rather than restated per primitive for the same reason `resolveTarget` lives here
81
+ * rather than in each caller: `target:`/`scope:` is one convention across the library, and six
82
+ * copies of a `keywords` list is six places for it to drift.
83
+ *
84
+ * `default: ''` is load-bearing, not a style choice. `readParams` (`core/js-params.ts`) seeds
85
+ * `out[name] = spec.default` for *every* declared parameter before reading authored values, and
86
+ * `createParams`' `text` accessor uses `??`, so a non-empty schema default would make the
87
+ * caller-supplied fallback in {@link scopeParam} unreachable. `scroll-spy` is one primitive with
88
+ * one `ParameterSchema` whose two forms need opposite defaults (`'page'` for the per-section form,
89
+ * `'self'` for the container form), so "unset" has to be spellable — which is exactly why `target`
90
+ * itself is already declared `default: ''` on every primitive that owns the parameter.
91
+ *
92
+ * `type: 'keyword'`, not `'text'`: a `text` param accepts any string that survives the escape
93
+ * screen, so `scope:pgae` would be accepted in silence and then read as unset. `keyword` is the
94
+ * type whose `keywords` list is the *whole* grammar, so a typo is named. The cost is that
95
+ * `--kui-scope` reaches
96
+ * `element.style` when authored, where nothing reads it; an inert custom property is a much smaller
97
+ * price than a typo that fails silently.
98
+ */
99
+ export declare const SCOPE_PARAM: ParamSpec;
100
+ /**
101
+ * Read `scope:`, falling back to the resolution site's own historical default.
102
+ *
103
+ * The fallback is per *call site*, not per primitive, because `scroll-spy` resolves the same
104
+ * declared parameter document-wide in one form and descendant-scoped in the other.
105
+ *
106
+ * @param params - The effect's validated parameters.
107
+ * @param fallback - What this site scoped to before `scope:` existed. Passing today's behaviour
108
+ * here is what makes declaring the parameter a no-op for existing markup.
109
+ * @returns The authored scope, or `fallback` when unset or unrecognised.
110
+ * @complexity O(1) time and space.
111
+ * @overallScore 100
112
+ */
113
+ export declare function scopeParam(params: EffectParams, fallback: TargetScope): TargetScope;
114
+ /**
115
+ * Resolve a validated `target:` selector under a scope.
116
+ *
117
+ * {@link resolveTarget} has already run: this does no validation of its own, so an invalid or
118
+ * document-wide selector must have been rejected before it gets here.
119
+ *
120
+ * @param el - The authored host. The search root under `'self'`.
121
+ * @param ctx - Prepare context, for `doc` — the search root under `'page'`.
122
+ * @param selector - A validated, non-empty `target:` value.
123
+ * @param scope - Which tree to search.
124
+ * @returns The matches, in document order.
125
+ * @complexity O(n) time and space in matches; the query itself is the DOM's.
126
+ * @overallScore 100
127
+ */
128
+ export declare function queryScoped(el: Element, ctx: Pick<TargetContext, 'doc'>, selector: string, scope: TargetScope): Element[];
@@ -0,0 +1,45 @@
1
+ import type { Reporter } from './reporter.js';
2
+ /** The subset of an observed binding's state this diagnostic reads and updates. */
3
+ export interface ThresholdReachabilityState {
4
+ /** Where to report an unreachable threshold. `undefined` means nowhere — nothing to check for. */
5
+ reporter?: Reporter;
6
+ /**
7
+ * Whether this element has ever been geometrically inside the root, independent of whether it
8
+ * ever met the authored threshold. Tells a genuine leave (toured through, never got there) apart
9
+ * from the first delivery of an element that starts outside the root (never arrived yet) — both
10
+ * report `isIntersecting: false`, and only one of them is evidence of anything.
11
+ */
12
+ sawIntersecting: boolean;
13
+ /** Whether the threshold was ever actually met. A binding that got here has never met it. */
14
+ entered: boolean;
15
+ /** Set once this has warned, so a repeated tour past the same unreachable threshold stays quiet. */
16
+ thresholdWarned: boolean;
17
+ }
18
+ /**
19
+ * Say so when a full transit closes without an authored threshold ever being met, and the target's
20
+ * own geometry is why it never can be: it is larger than the root it scrolls through, so its
21
+ * intersection ratio has a hard ceiling below what was asked for. See {@link maxReachableRatio}.
22
+ *
23
+ * Checked only on a genuine leave — `!entry.isIntersecting` once {@link ThresholdReachabilityState.sawIntersecting}
24
+ * already recorded a real entry — rather than on whichever delivery arrives first. A target's
25
+ * measured size is a runtime fact, not an authored one, and the very first delivery for a freshly
26
+ * observed element can land before a lazy image has decoded, an accordion has expanded, or a web
27
+ * font has swapped in — any of which can measure a box taller *right now* than it will be once the
28
+ * page settles. A completed transit — inside the root and back out — gives layout the whole scroll
29
+ * past it to settle before its geometry is trusted for a diagnostic that, once given, cannot be
30
+ * unsaid. The cost is a symmetrical blind spot: a target that is too tall only *during* one transit
31
+ * (a mid-scroll resize) keeps quiet until the next full transit re-checks it — the same shape of
32
+ * tradeoff `meetsThreshold`'s own caveat already accepts for the threshold itself.
33
+ *
34
+ * An element that starts already outside the root also delivers `isIntersecting: false` on its very
35
+ * first entry — "hasn't arrived yet," not "toured through and failed" — which is exactly what
36
+ * `sawIntersecting` exists to tell apart from a real leave.
37
+ *
38
+ * Warns once per element (`thresholdWarned`), not once per delivery or per transit: an element that
39
+ * tours past its unreachable threshold repeatedly (a reader scrolling up and down) would otherwise
40
+ * repeat the same diagnostic every time.
41
+ *
42
+ * @complexity O(1) time, O(1) space.
43
+ * @overallScore 100
44
+ */
45
+ export declare function checkThresholdReachability(binding: ThresholdReachabilityState, entry: IntersectionObserverEntry, ratio: number): void;
@@ -0,0 +1,9 @@
1
+ /** The ancestor marker read by library animation creation and the showcase speed control. */
2
+ export declare const TIME_SCALE_ATTR = "data-kui-time-scale";
3
+ /**
4
+ * Read the nearest time scale, including a marker on the element itself.
5
+ *
6
+ * @complexity O(h) in ancestor depth; O(1) space.
7
+ * @overallScore 100
8
+ */
9
+ export declare function timeScaleOf(el: Element): number;
@@ -0,0 +1,141 @@
1
+ import type { InstanceControl } from './types.js';
2
+ /**
3
+ * The four crossings, and what an author can ask for at each.
4
+ *
5
+ * A scroll trigger has four distinct moments, not two. Travelling down the page an element *enters*
6
+ * the viewport and later *leaves* it out of the top; travelling back up it re-enters from the top
7
+ * and leaves again out of the bottom. `data-kui-on="enter/leave"` covers two of them, with the only
8
+ * two verbs being "play" and "play backwards" — so the thing an author most often wants from a
9
+ * scroll trigger, *pause where you are and carry on from there when I come back*, could not be
10
+ * written at all.
11
+ *
12
+ * This module is the vocabulary: what the four crossings are called, what the eight verbs mean, and
13
+ * how one authored string becomes a verb per crossing. It touches no DOM and holds no state.
14
+ * `activation.ts` decides *which* crossing happened — it is the only thing holding the observer
15
+ * entry that can tell — and `animator.ts` owns the element and supplies the two element-level
16
+ * transitions the verbs are built from. Neither of them has to know the vocabulary, and this file
17
+ * does not have to know either of them.
18
+ *
19
+ * **Nothing here animates anything.** Every verb is composed from `InstanceControl` —
20
+ * `pause`/`resume`/`seek`, which `control.ts` has exposed as public API since runtime control
21
+ * shipped — plus `activate` and `reverse`, which only the animator can own because they are
22
+ * transitions of the element's whole state machine rather than of one playhead.
23
+ */
24
+ /**
25
+ * One of the four moments a scroll crossing can happen at, named from the reader's direction of
26
+ * travel rather than the element's: `enter` and `leave` happen while scrolling *down*, the two
27
+ * `-back` crossings while scrolling back *up*.
28
+ *
29
+ * Hyphenated rather than camel-cased because these names are quoted back to authors in warnings,
30
+ * and every other word this library asks an author to read is hyphenated.
31
+ */
32
+ export type Crossing = 'enter' | 'leave' | 'enter-back' | 'leave-back';
33
+ /** The four crossings in the order an author writes them — GSAP's `toggleActions` order. */
34
+ export declare const CROSSINGS: readonly Crossing[];
35
+ /**
36
+ * What to do at a crossing.
37
+ *
38
+ * The same eight GSAP's `toggleActions` takes, and the same words, because an author arriving from
39
+ * it should not have to learn a second vocabulary for an identical idea. What each one means here
40
+ * is stated in {@link applyToggleVerb}, which is also where the ones that cannot be honest about an
41
+ * element that has not started yet are handled.
42
+ */
43
+ export type ToggleVerb = 'play' | 'pause' | 'resume' | 'reverse' | 'reset' | 'restart' | 'complete' | 'none';
44
+ /** A verb per crossing. Every slot is always present; an unwritten one is `none`. */
45
+ export type ToggleActions = Readonly<Record<Crossing, ToggleVerb>>;
46
+ /**
47
+ * Parse an authored `actions:` value into a verb per crossing.
48
+ *
49
+ * Total by construction: an unrecognised verb becomes `none` rather than failing the element, and
50
+ * every rejection is named. Trailing slots the author left off are `none` too, so `actions:play` is
51
+ * "play on the way in and nothing else" — which is both GSAP's own default and this library's
52
+ * existing behaviour for an unpaired `on:enter`.
53
+ *
54
+ * @param value - Authored text, e.g. `play/pause/resume/none`.
55
+ * @param warnings - Sink for the diagnostics. Optional so a pure parse can ignore them.
56
+ * @complexity O(1) time and space — at most four tokens.
57
+ * @overallScore 100
58
+ */
59
+ export declare function parseToggleActions(value: string, warnings?: string[]): ToggleActions;
60
+ /**
61
+ * Diagnose an authored `actions:` value without a document — the shape-only half.
62
+ *
63
+ * Mirrors `validateActivation`'s contract exactly, including where it stops: this is the part
64
+ * `parse.ts` can run with nothing but the attribute text. Whether the *element* can honour the
65
+ * verbs needs its compiled plan, and lives in {@link warnAboutToggleActions}.
66
+ *
67
+ * @returns Zero warnings when every token is a verb.
68
+ * @complexity O(1) time and space.
69
+ * @overallScore 100
70
+ */
71
+ export declare function validateToggleActions(value: string): string[];
72
+ /** What {@link warnAboutToggleActions} needs to know about the element, with no DOM in it. */
73
+ export interface ToggleDiagnostics {
74
+ actions: ToggleActions;
75
+ /** Whether this element's activation is delivered by the intersection observer at all. */
76
+ observed: boolean;
77
+ /** The authored activation, quoted back when it is not an observed one. */
78
+ activation: string;
79
+ /** Names of the composed effects rendered in JavaScript — they expose no playhead. */
80
+ jsEffectNames: readonly string[];
81
+ /** Whether the element's progress belongs to the scroller rather than to a clock. */
82
+ progressDriven: boolean;
83
+ }
84
+ /**
85
+ * Report every way an authored `actions:` cannot do what it says.
86
+ *
87
+ * All three of these produce a knob that exists and does nothing, which this codebase treats as
88
+ * worse than a missing knob — and all three are invisible from the markup, because the element
89
+ * still animates, just not the way the attribute claims.
90
+ *
91
+ * @returns One message per problem; empty when the element can honour every verb.
92
+ * @complexity O(1) time and space — four crossings.
93
+ * @overallScore 100
94
+ */
95
+ export declare function warnAboutToggleActions(request: ToggleDiagnostics): string[];
96
+ /** The element a verb is being applied to, reduced to what the verbs actually need. */
97
+ export interface ToggleTarget {
98
+ /**
99
+ * Whether the element has been activated at all.
100
+ *
101
+ * The distinction two of the verbs cannot be honest without. An element that has never started is
102
+ * already sitting at its from-state, so `reset` there is a no-op rather than a seek, and `resume`
103
+ * means *start* rather than *unpause* — going straight to the ledger's play-state would open the
104
+ * compiled gate behind the animator's back, leaving `data-kui-state` reading `ready` on a visibly
105
+ * running element and no `kui:start` ever dispatched.
106
+ */
107
+ started: boolean;
108
+ /** The element's reachable playheads. Empty for a JS-rendered or scroll-driven element. */
109
+ controls: readonly InstanceControl[];
110
+ /** Start, or turn a reversing playhead back around — `Animator.activate`. */
111
+ activate(): void;
112
+ /** Play out backwards to the from-state — `Animator.reverseFrom`. */
113
+ reverse(): void;
114
+ }
115
+ /**
116
+ * Do one verb.
117
+ *
118
+ * Every branch is composed from things that already existed. `play` and `reverse` are the element's
119
+ * two directional transitions, which only the animator owns; the other five are `InstanceControl`
120
+ * calls. Nothing here touches an `Animation` object, and nothing here decides *when* — that is the
121
+ * observer's job.
122
+ *
123
+ * The three jump verbs are worth stating precisely, because "a jump" and "a state machine" are easy
124
+ * to get wrong together:
125
+ *
126
+ * - **`reset`** rewinds to the from-state and stops there. On an element that never started there
127
+ * is nothing to rewind — it is already at the from-state — so this does nothing at all rather
128
+ * than seeking playheads still sitting behind the compiled gate.
129
+ * - **`restart`** rewinds and keeps going. It activates first, so an element crossing for the first
130
+ * time starts properly — with its `kui:start`, its state attribute and its settle — rather than
131
+ * being seeked while still gated. On an already-running element `activate` returns at its own
132
+ * guard, so no second settle is armed and the seek simply replays the run.
133
+ * - **`complete`** jumps to the end state, and activates first for the same reason. It deliberately
134
+ * does *not* pause afterwards: an animation seeked to its end while still playing reaches
135
+ * `finished` on its own, and that is what resolves the settle and dispatches `kui:finish`.
136
+ * Pausing it there would strand the element at `running` forever with nothing moving.
137
+ *
138
+ * @complexity O(n) time in the element's controllable instances; O(1) space.
139
+ * @overallScore 100
140
+ */
141
+ export declare function applyToggleVerb(verb: ToggleVerb, target: ToggleTarget): void;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Which way the reader is travelling, and which side of a scroll root that puts things on.
3
+ *
4
+ * Split out of `activation.ts` because it answers a question about the *reader* rather than about
5
+ * any element or any authored spec: one page-wide signal, sampled from one listener, that the
6
+ * four-way crossing delivery consults and nothing else in the binder cares about.
7
+ */
8
+ /**
9
+ * Which side of the observer's root an element sits on, in the reader's direction of travel:
10
+ * `before` is the side already scrolled past, `after` the side not reached yet.
11
+ *
12
+ * Named by travel rather than by geometry so one word covers both axes — `before` is above a
13
+ * vertically scrolled root and to the left of a horizontally scrolled one.
14
+ */
15
+ export type RootSide = 'before' | 'after';
16
+ /** Which way the reader is travelling, phrased as the side an element entering *now* came from. */
17
+ export interface TravelTracker {
18
+ /**
19
+ * The side an element entering at `time` must have arrived from, or `undefined` when the reader's
20
+ * travel does not explain a delivery at that moment.
21
+ */
22
+ arrivedFrom(time: number): RootSide | undefined;
23
+ /** Start listening, if this is the first four-way binding to ask. */
24
+ retain(): void;
25
+ /** Stop listening, if this was the last. */
26
+ release(): void;
27
+ /** Stop listening regardless of the count, for a binder torn down with bindings still on it. */
28
+ reset(): void;
29
+ }
30
+ /**
31
+ * Track which way the reader is travelling, so an entering delivery can be classified by what is
32
+ * happening *now* rather than by memory of a crossing that may never have been delivered.
33
+ *
34
+ * `deliverCrossing` used to answer "first entry or re-entry?" purely from `binding.outside`, the
35
+ * side recorded by the last *leaving* delivery. A scroll that lands in a single frame — an
36
+ * `<a href="#anchor">` click, `scrollIntoView()`, `scrollTo({ behavior: 'instant' })`, scroll
37
+ * restoration — carrying the element from "not intersecting" straight past to "not intersecting" on
38
+ * the far side changes neither `isIntersecting` nor the threshold ratio at any frame the browser
39
+ * samples, so the observer delivers *nothing at all* and the remembered side is left describing a
40
+ * position the reader is no longer anywhere near. The next real delivery is then classified against
41
+ * it and comes out backwards: measured in Chrome, a reader scrolling back up through a skipped
42
+ * element got `enter` where `enter-back` was authored. A denser threshold array does not help,
43
+ * because the browser paints no intermediate frame for it to sample; nor does re-reading the
44
+ * geometry, because an entering element is inside the root and so is on no side to read.
45
+ *
46
+ * The reader's direction of travel answers it without needing to know anything about the skipped
47
+ * crossing, and the two agree wherever nothing was skipped, so the ordinary path is unchanged.
48
+ *
49
+ * One listener for every binding on the page, mirroring `activation.ts`'s one-observer-per-threshold:
50
+ * direction is a property of the reader, not of an element, so per-element listeners would buy
51
+ * nothing for their cost. It is retained only by four-way bindings — a page of plain `on:enter`
52
+ * effects adds no listener at all.
53
+ *
54
+ * @returns A tracker whose `retain`/`release` ref-count the single shared listener.
55
+ * @complexity O(1) time per scroll event, independent of how many elements are bound; O(s) space in
56
+ * the number of distinct scrollers the reader has actually touched.
57
+ * @overallScore 100
58
+ */
59
+ export declare function createTravelTracker(): TravelTracker;