kuinetic 0.1.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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/esm/chunk-LWS4OSLX.mjs +730 -0
  4. package/dist/esm/chunk-QZIJ7WZI.mjs +3312 -0
  5. package/dist/esm/chunk-R3TGDJKA.mjs +1264 -0
  6. package/dist/esm/core/index.mjs +34 -0
  7. package/dist/esm/effects/index.mjs +27 -0
  8. package/dist/esm/index.mjs +52 -0
  9. package/dist/kuinetic.all.js +5291 -0
  10. package/dist/kuinetic.css +2338 -0
  11. package/dist/kuinetic.js +5281 -0
  12. package/dist/types/core/activation.d.ts +21 -0
  13. package/dist/types/core/animator.d.ts +216 -0
  14. package/dist/types/core/attrs.d.ts +15 -0
  15. package/dist/types/core/capabilities.d.ts +19 -0
  16. package/dist/types/core/channels.d.ts +25 -0
  17. package/dist/types/core/compile.d.ts +49 -0
  18. package/dist/types/core/dom-watcher.d.ts +35 -0
  19. package/dist/types/core/effect-context.d.ts +49 -0
  20. package/dist/types/core/element-config.d.ts +52 -0
  21. package/dist/types/core/flip.d.ts +80 -0
  22. package/dist/types/core/gesture.d.ts +99 -0
  23. package/dist/types/core/index.d.ts +14 -0
  24. package/dist/types/core/instances.d.ts +98 -0
  25. package/dist/types/core/js-effect-preparer.d.ts +46 -0
  26. package/dist/types/core/js-params.d.ts +124 -0
  27. package/dist/types/core/owned-styles.d.ts +44 -0
  28. package/dist/types/core/params.d.ts +65 -0
  29. package/dist/types/core/parse.d.ts +22 -0
  30. package/dist/types/core/path-morph.d.ts +83 -0
  31. package/dist/types/core/play.d.ts +48 -0
  32. package/dist/types/core/registry.d.ts +38 -0
  33. package/dist/types/core/reporter.d.ts +35 -0
  34. package/dist/types/core/scroll-scheduler.d.ts +153 -0
  35. package/dist/types/core/spring.d.ts +83 -0
  36. package/dist/types/core/stagger.d.ts +19 -0
  37. package/dist/types/core/style-plan.d.ts +56 -0
  38. package/dist/types/core/types.d.ts +239 -0
  39. package/dist/types/effects/catalog/ambient.d.ts +18 -0
  40. package/dist/types/effects/catalog/core.d.ts +19 -0
  41. package/dist/types/effects/catalog/feedback.d.ts +21 -0
  42. package/dist/types/effects/catalog/index.d.ts +21 -0
  43. package/dist/types/effects/catalog/interaction-shared.d.ts +56 -0
  44. package/dist/types/effects/catalog/interaction.d.ts +22 -0
  45. package/dist/types/effects/catalog/media.d.ts +13 -0
  46. package/dist/types/effects/catalog/numbers-shared.d.ts +103 -0
  47. package/dist/types/effects/catalog/numbers.d.ts +31 -0
  48. package/dist/types/effects/catalog/shared.d.ts +3 -0
  49. package/dist/types/effects/catalog/text-shared.d.ts +180 -0
  50. package/dist/types/effects/catalog/text.d.ts +17 -0
  51. package/dist/types/effects/forms/index.d.ts +14 -0
  52. package/dist/types/effects/forms/primitives.d.ts +32 -0
  53. package/dist/types/effects/gestures/index.d.ts +19 -0
  54. package/dist/types/effects/gestures/primitives.d.ts +2 -0
  55. package/dist/types/effects/index.d.ts +19 -0
  56. package/dist/types/effects/layout/index.d.ts +12 -0
  57. package/dist/types/effects/layout/presets.d.ts +9 -0
  58. package/dist/types/effects/layout/primitives.d.ts +2 -0
  59. package/dist/types/effects/navigation/index.d.ts +30 -0
  60. package/dist/types/effects/scroll-mechanics/index.d.ts +14 -0
  61. package/dist/types/effects/scroll-mechanics/presets.d.ts +6 -0
  62. package/dist/types/effects/scroll-mechanics/primitives.d.ts +2 -0
  63. package/dist/types/effects/scroll-mechanics/tracker.d.ts +55 -0
  64. package/dist/types/effects/shared.d.ts +26 -0
  65. package/dist/types/effects/svg/index.d.ts +13 -0
  66. package/dist/types/effects/three-d/index.d.ts +20 -0
  67. package/dist/types/index.d.ts +16 -0
  68. package/package.json +78 -0
@@ -0,0 +1,14 @@
1
+ export { createActivationBinder } from './activation.js';
2
+ export type { ActivationBinder, ActivationBinderOptions } from './activation.js';
3
+ export { Animator, createAnimator, ATTR } from './animator.js';
4
+ export type { AnimatorOptions } from './animator.js';
5
+ export { detect } from './capabilities.js';
6
+ export type { Capabilities } from './capabilities.js';
7
+ export { play, resolveTargets, toAttributeValue } from './play.js';
8
+ export type { PlaybackHandle, PlayOptions, Target } from './play.js';
9
+ export { Registry } from './registry.js';
10
+ export type { ResolvedEffect } from './registry.js';
11
+ export { collectingReporter, consoleReporter, silentReporter } from './reporter.js';
12
+ export type { CollectingReporter, Reporter } from './reporter.js';
13
+ export { CHANNEL, inertInstance } from './types.js';
14
+ export type { Activation, Channel, Cleanup, EffectInstance, EffectParams, ParamSpec, ParameterSchema, PerfClass, PrepareContext, Preset, Primitive, ReducedMotionPolicy, Timeline, } from './types.js';
@@ -0,0 +1,98 @@
1
+ import type { StyleLedger } from './owned-styles.js';
2
+ import type { Cleanup, EffectInstance } from './types.js';
3
+ /**
4
+ * Wrap a CSS-rendered effect.
5
+ *
6
+ * Gating is `animation-play-state` rather than a class toggle: `animation-fill-mode: both`
7
+ * already holds the from-state, so there is no flash between compilation and activation.
8
+ *
9
+ * Repeat activation of the same instance and a fresh instance's first activation both can meet a
10
+ * browser-level animation already sitting `finished`, but they need opposite responses. A second
11
+ * `activate()` on the same instance is a click-gated toggle (a card flip): reversing playback is
12
+ * the correct repeat — the property write only matters on the paused-to-running edge, so writing
13
+ * the same value again is a no-op the browser silently ignores, which is what made two-state
14
+ * effects look permanently stuck. A fresh instance's *first* activation meeting a `finished`
15
+ * animation instead means the browser never actually tore down the previous one (see
16
+ * `restartCssAnimation`) — replay wants that to run forward from the start, not reverse, so it
17
+ * gets the restart path instead of `.reverse()`.
18
+ *
19
+ * @param el - Element carrying the compiled animation.
20
+ * @param ledger - Ledger owning the play-state write.
21
+ * @param animationNames - Exact keyframe names emitted by the compiled plan.
22
+ * @returns A lifecycle handle over the element's owned CSS animations.
23
+ * @complexity O(a) per call in the number of running animations; O(1) space.
24
+ * @overallScore 100
25
+ */
26
+ export declare function createCssInstance(el: Element, ledger: StyleLedger, animationNames: readonly string[]): EffectInstance;
27
+ /**
28
+ * What a *finite* JS primitive's setup returns instead of a bare teardown.
29
+ *
30
+ * A continuous primitive — a drag handler, a pin, an ambient loop — has no end, so it returns a
31
+ * plain `Cleanup` and keeps the immediately-resolved `finished` every caller composing
32
+ * `Promise.all` over an element's instances already relies on. Only an effect that genuinely
33
+ * completes needs to say so, and only that effect pays for it.
34
+ */
35
+ export interface TimedSetup {
36
+ cleanup: Cleanup;
37
+ /**
38
+ * Resolves when the effect's own work is done. A setup that loops forever should hand over a
39
+ * promise that never resolves — the same contract an `animation-iteration-count: infinite` CSS
40
+ * effect already has, since its `Animation.finished` never resolves either.
41
+ */
42
+ finished: Promise<void>;
43
+ /** Jump to the end state, for `EffectInstance.finish()`. */
44
+ finish?: () => void;
45
+ }
46
+ /** A deferred setup's return: a teardown, or a teardown plus the effect's own completion. */
47
+ export type SetupResult = Cleanup | TimedSetup;
48
+ /**
49
+ * Turn setup-that-also-starts into a properly gated instance.
50
+ *
51
+ * Most JS primitives are naturally written as "wire it up and go" — subscribe to the scheduler,
52
+ * attach listeners, write the first style. Deferring that whole body until `activate()` is what
53
+ * makes them obey `on:enter`, `on:click`, `manual`, and `reducedMotion: 'disable'` without each
54
+ * primitive having to implement gating itself.
55
+ *
56
+ * @param setup - Runs on activation; returns its own teardown, optionally with its completion.
57
+ * @returns An instance that does nothing until activated.
58
+ * @complexity O(1) beyond the setup itself.
59
+ * @overallScore 100
60
+ */
61
+ export declare function deferredInstance(setup: () => SetupResult): EffectInstance;
62
+ /**
63
+ * Wrap a `(el, params, ctx) => SetupResult`-shaped setup function as a deferred
64
+ * `Primitive['prepare']`.
65
+ *
66
+ * Every JS primitive's `prepare` is `deferredInstance(() => setup(...args))` — this names that
67
+ * composition once instead of re-deriving it at each of the library's fourteen call sites.
68
+ *
69
+ * @complexity O(1) time and space beyond the wrapped call.
70
+ * @overallScore 100
71
+ */
72
+ export declare function deferPrepare<Args extends unknown[]>(setup: (...args: Args) => SetupResult): (...args: Args) => EffectInstance;
73
+ export interface JsInstanceHooks {
74
+ /** Start the effect. Never called before the animator's gate opens. */
75
+ activate(): void;
76
+ cancel?(): void;
77
+ finish?(): void;
78
+ /** Release listeners, observers, subscriptions, and inserted nodes. */
79
+ destroy: Cleanup;
80
+ /**
81
+ * Resolve when the effect completes. Continuous effects may simply never resolve.
82
+ *
83
+ * Read on every access rather than captured once, so a setup that only learns its own completion
84
+ * at activation time can swap in a real promise — the animator and `play()` both read
85
+ * `finished` after `activate()`, never before.
86
+ */
87
+ finished?(): Promise<void>;
88
+ }
89
+ /**
90
+ * Build a JS-rendered instance from a primitive's hooks, filling in the optional operations.
91
+ *
92
+ * Keeps primitives free of lifecycle boilerplate while still guaranteeing the animator every
93
+ * operation it needs.
94
+ *
95
+ * @complexity O(1) time and space.
96
+ * @overallScore 100
97
+ */
98
+ export declare function createJsInstance(hooks: JsInstanceHooks): EffectInstance;
@@ -0,0 +1,46 @@
1
+ import type { Capabilities } from './capabilities.js';
2
+ import type { CompiledPlan } from './compile.js';
3
+ import type { StyleLedger } from './owned-styles.js';
4
+ import type { Reporter } from './reporter.js';
5
+ import type { ScrollRoot, ScrollScheduler } from './scroll-scheduler.js';
6
+ import type { EffectInstance } from './types.js';
7
+ /** Everything `JsEffectPreparer.prepare` needs, grouped so the call site reads as one request. */
8
+ export interface PrepareJsEffectsRequest {
9
+ el: Element;
10
+ plan: CompiledPlan;
11
+ signal: AbortSignal;
12
+ ledger: StyleLedger;
13
+ }
14
+ /**
15
+ * Wires up an element's JS-rendered effects and hands each one the context it needs to run.
16
+ *
17
+ * Extracted from `Animator` as an injected collaborator — same shape as `binder` or `scheduler` —
18
+ * because this pair only ever reads already-injected environment collaborators
19
+ * (`scheduler`/`rootResolver`/`capabilities`/`reporter`/`respectReducedMotion`), never the
20
+ * animator's own lifecycle state.
21
+ */
22
+ export interface JsEffectPreparer {
23
+ /**
24
+ * Run each JS-rendered effect's setup, isolating failures so one broken effect cannot abort the
25
+ * rest of the element.
26
+ *
27
+ * @returns Instances for every effect that initialised successfully.
28
+ * @complexity O(e) time in JS-rendered effects; O(e) space.
29
+ * @overallScore 100
30
+ */
31
+ prepare(request: PrepareJsEffectsRequest): EffectInstance[];
32
+ }
33
+ export interface JsEffectPreparerOptions {
34
+ scheduler: ScrollScheduler;
35
+ rootResolver: (el: Element) => ScrollRoot;
36
+ capabilities: Capabilities;
37
+ reporter: Reporter;
38
+ respectReducedMotion: boolean;
39
+ }
40
+ /**
41
+ * Build a `JsEffectPreparer` closed over one animator's collaborators.
42
+ *
43
+ * @complexity O(1) time and space beyond the closure it returns.
44
+ * @overallScore 100
45
+ */
46
+ export declare function createJsEffectPreparer(options: JsEffectPreparerOptions): JsEffectPreparer;
@@ -0,0 +1,124 @@
1
+ import type { EffectParams, EffectSpec, EffectTiming, ParameterSchema } from './types.js';
2
+ /**
3
+ * Parameter access for JS-rendered primitives.
4
+ *
5
+ * `core/params.resolveParams` exists for the CSS path: it returns values keyed by *custom
6
+ * property* and deliberately omits defaults, because defaults belong in the stylesheet's `var()`
7
+ * fallback. A JS primitive needs the opposite shape — keyed by parameter name, defaults filled,
8
+ * and converted to numbers it can compute with. This module is that second reader. It reuses the
9
+ * same `validate` so untrusted author strings are screened once, by one implementation.
10
+ *
11
+ * See docs/v2-core-requests.md: `Animator.prepareJsEffects` currently hands `prepare` the raw,
12
+ * unvalidated `spec.params`, so every JS primitive must run this itself.
13
+ */
14
+ /** Lengths resolve against the environment, so the environment is passed in rather than read. */
15
+ export interface LengthBasis {
16
+ viewportWidth: number;
17
+ viewportHeight: number;
18
+ /** Basis for `%` values — what "100%" means for this particular parameter. */
19
+ percentBasis: number;
20
+ /** Basis for `em`; `rem` uses `rootFontSize`. */
21
+ fontSize: number;
22
+ rootFontSize: number;
23
+ }
24
+ /** A basis usable when a parameter is known to carry only absolute units. */
25
+ export declare const ABSOLUTE_BASIS: LengthBasis;
26
+ /**
27
+ * Validate authored parameters against a schema and fill in every declared default.
28
+ *
29
+ * Unlike the CSS path this returns *every* declared parameter, because a JS primitive branches on
30
+ * values it must always have. Rejected values fall back to the declared default and warn, so one
31
+ * bad attribute can never leave a primitive holding an unvalidated string.
32
+ *
33
+ * @param authored - Raw, untrusted values from the attribute.
34
+ * @param schema - The primitive's declared parameters.
35
+ * @param warn - Diagnostic sink, called once per rejected or unknown parameter.
36
+ * @returns Every declared parameter name mapped to a validated string value.
37
+ * @complexity O(p * n) time in parameter count and value length; O(p) space.
38
+ * @overallScore 100
39
+ */
40
+ export declare function readParams(authored: Record<string, string>, schema: ParameterSchema, warn: (message: string) => void): Record<string, string>;
41
+ /**
42
+ * Convert a validated CSS time to milliseconds.
43
+ *
44
+ * @param value - A value already accepted as `type: 'time'`, e.g. `400ms` or `1.5s`.
45
+ * @param fallback - Returned when the value is not a plain time (a `calc()`, for instance).
46
+ * @returns Milliseconds.
47
+ * @complexity O(n) time in value length; O(1) space.
48
+ * @overallScore 100
49
+ */
50
+ export declare function toMilliseconds(value: string, fallback?: number): number;
51
+ /**
52
+ * Convert a validated CSS length to pixels.
53
+ *
54
+ * Viewport- and percentage-relative units need context, which is why the basis is a parameter
55
+ * rather than something this module reads off `window`: the same helper then resolves a length
56
+ * against a nested scroll container as easily as against the viewport, and tests need no layout.
57
+ *
58
+ * `calc()` is intentionally not evaluated — a second CSS expression engine is not worth owning.
59
+ * Such values return the fallback, which is visible and debuggable rather than silently wrong.
60
+ *
61
+ * @param value - A value already accepted as `type: 'length'`.
62
+ * @param basis - Viewport, font, and percentage context.
63
+ * @param fallback - Returned for `calc()` and unrecognised units.
64
+ * @returns Pixels.
65
+ * @complexity O(n) time in value length; O(1) space.
66
+ * @overallScore 100
67
+ */
68
+ export declare function toPixels(value: string, basis: LengthBasis, fallback?: number): number;
69
+ /**
70
+ * Convert a validated numeric parameter.
71
+ *
72
+ * @param value - A value already accepted as `type: 'number'` or `type: 'percentage'`.
73
+ * @param fallback - Returned when the value is not a bare number or percentage.
74
+ * @returns The number; percentages are returned as a 0–1 ratio.
75
+ * @complexity O(n) time in value length; O(1) space.
76
+ * @overallScore 100
77
+ */
78
+ export declare function toNumber(value: string, fallback?: number): number;
79
+ /** Whether a keyword parameter is set to its enabling value. */
80
+ export declare function isEnabled(value: string, enabling?: string): boolean;
81
+ /**
82
+ * Read one effect segment's positional timing into the shape JS primitives consume.
83
+ *
84
+ * The CSS renderer hands these straight to `animation-duration`/`-delay`/`-timing-function`, where
85
+ * the browser does the screening. Nothing screened them on the JS path, so they are converted to
86
+ * numbers (and the easing validated) here instead of anywhere a primitive might forget.
87
+ *
88
+ * @param spec - The parsed effect segment.
89
+ * @param warn - Diagnostic sink, called once per rejected value.
90
+ * @complexity O(n) time in value length; O(1) space.
91
+ * @overallScore 100
92
+ */
93
+ export declare function readEffectTiming(spec: Pick<EffectSpec, 'duration' | 'delay' | 'easing'>, warn: (message: string) => void): EffectTiming;
94
+ /**
95
+ * How long an unstepped effect should run, preferring the segment's positional time over the
96
+ * same-named parameter.
97
+ *
98
+ * `count 3s` and `count duration:3s` are two spellings of one intent, and the positional one is
99
+ * what `play()` emits, so it cannot be the spelling that gets dropped. `stepMsFor` is the
100
+ * equivalent for effects whose authored duration divides across a tick count instead.
101
+ *
102
+ * @param fallback - The primitive's own default, used when the author wrote neither spelling.
103
+ * @complexity O(1) time and space.
104
+ * @overallScore 100
105
+ */
106
+ export declare function effectDurationMs(params: EffectParams, fallback: number): number;
107
+ /**
108
+ * Wrap a validated record in the reader primitives consume.
109
+ *
110
+ * @param values - Output of `readParams`; every declared parameter is present.
111
+ * @param timing - Author timing for the segment; empty when none was written.
112
+ * @returns A reader with per-type accessors.
113
+ * @complexity O(1) per accessor call; O(1) space beyond the record.
114
+ * @overallScore 100
115
+ */
116
+ export declare function createParams(values: Record<string, string>, timing?: EffectTiming): EffectParams;
117
+ /**
118
+ * Validate authored parameters and wrap them in a reader — the single entry point the animator
119
+ * uses for JS-rendered effects.
120
+ *
121
+ * @complexity O(p * n) time in parameter count and value length; O(p) space.
122
+ * @overallScore 100
123
+ */
124
+ export declare function readEffectParams(authored: Record<string, string>, schema: ParameterSchema, warn: (message: string) => void, timing?: EffectTiming): EffectParams;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Owned-write ledger.
3
+ *
4
+ * The runtime writes inline custom properties, animation longhands, and timeline properties, but
5
+ * previously tore down by removing three attributes. Recompiling `fade-up distance:80px` into
6
+ * `zoom-in` therefore left `--kui-distance` behind, and destroying an animator left every
7
+ * animation installed.
8
+ *
9
+ * A ledger records what this library wrote *and what was there before*, so teardown restores the
10
+ * consumer's own inline styles rather than deleting them. That distinction matters: several JS
11
+ * primitives were previously clearing author-set `translate` and `scroll-snap-align` values they
12
+ * did not own.
13
+ */
14
+ export interface StyleLedger {
15
+ /** Write a property, remembering the value it replaced. */
16
+ set(property: string, value: string): void;
17
+ /** Record a property this ledger will restore, without writing anything yet. */
18
+ claim(property: string): void;
19
+ /** Restore every recorded property to the value it had before this ledger touched it. */
20
+ restore(): void;
21
+ /** Properties currently owned. Diagnostics and leak assertions. */
22
+ owned(): string[];
23
+ }
24
+ /**
25
+ * Create a ledger over one element's inline style.
26
+ *
27
+ * @param el - Element whose inline style is managed.
28
+ * @returns A ledger that can restore the element to its pre-effect inline state.
29
+ * @complexity O(1) per write; O(n) space and O(n) time to restore, in properties written.
30
+ * @overallScore 100
31
+ */
32
+ export declare function createStyleLedger(el: Element): StyleLedger;
33
+ export interface AttributeLedger {
34
+ set(name: string, value: string): void;
35
+ restore(): void;
36
+ }
37
+ /**
38
+ * The same discipline for attributes, so a library-stamped attribute never clobbers an authored
39
+ * one permanently.
40
+ *
41
+ * @complexity O(1) per write; O(n) to restore.
42
+ * @overallScore 100
43
+ */
44
+ export declare function createAttributeLedger(el: Element): AttributeLedger;
@@ -0,0 +1,65 @@
1
+ import type { ParamSpec, ParameterSchema, ResolvedParams } from './types.js';
2
+ export interface ValidationResult {
3
+ value: string;
4
+ ok: boolean;
5
+ reason?: string;
6
+ }
7
+ /**
8
+ * Validate one authored parameter value against its declared type.
9
+ *
10
+ * @param raw - Author-supplied value, untrusted.
11
+ * @param spec - The parameter's declared type, default, and target custom property.
12
+ * @returns The accepted value, or the declared default with a reason when rejected. Never throws
13
+ * and never returns an unvalidated string.
14
+ * @complexity O(n) time in value length; O(1) space.
15
+ * @overallScore 100
16
+ */
17
+ export declare function validate(raw: string, spec: ParamSpec): ValidationResult;
18
+ /**
19
+ * Whether an authored value stays on the page's own origin wherever a `src`-shaped `text`
20
+ * parameter is actually turned into a network request.
21
+ *
22
+ * `type: 'text'` is deliberately shape-free — see the module doc above — because it also carries
23
+ * CSS selectors and other non-URL strings that have no notion of "origin" at all. So this is not
24
+ * part of `validate()`: a `text` value that is a URL pattern (media-scrub's frame `src`) is safe
25
+ * to accept lexically, but a *consumer of that value* must call this before ever assigning it to
26
+ * something that fetches, such as `<img>.src`.
27
+ *
28
+ * The threat: `data-kui` content is not always authored by the site owner — a CMS field, a
29
+ * comment, anything not trusted the way hand-written markup is — so an unconstrained `src`
30
+ * pattern turns the visitor's own browser into a same-origin-cookie-free but still
31
+ * attacker-directed request tool: exfiltration via path/query, third-party tracking pixels, or
32
+ * probing hosts on the victim's internal network that are unreachable from outside it. A
33
+ * Content-Security-Policy would mitigate this, but the library should not depend on the consumer
34
+ * having one.
35
+ *
36
+ * Only relative and root-relative paths pass. That is narrower than "any same-origin URL": a
37
+ * fully-qualified `https://this-very-site/…` is rejected too, on purpose, because nothing this
38
+ * library ships needs one — a root-relative path reaches the same resource — and accepting it
39
+ * would mean re-deriving "is this really the page's own origin" from `location` inside what is
40
+ * otherwise pure string validation, with all the parsing edge cases (`this-site.com.evil.com`,
41
+ * userinfo tricks, IDN lookalikes) that comparison invites. Rejecting every scheme uniformly,
42
+ * regardless of which host follows it, has no such edge cases.
43
+ *
44
+ * @param value - Author-supplied value already accepted by {@link validate} as `type: 'text'`.
45
+ * @returns Whether every request this value can produce is confined to the page's own origin.
46
+ * @complexity O(n) time in value length; O(1) space.
47
+ * @overallScore 100
48
+ */
49
+ export declare function isSameOriginPath(value: string): boolean;
50
+ /**
51
+ * Resolve authored parameters against a schema.
52
+ *
53
+ * Only values the author explicitly supplied are returned. Defaults are deliberately excluded:
54
+ * they live in the CSS `var()` fallback. Writing them to `element.style` would give inline custom
55
+ * properties precedence over consumer stylesheets and break the promise that a site's own CSS
56
+ * wins without `!important`.
57
+ *
58
+ * @param authored - Raw parameter values from the attribute or options object.
59
+ * @param schema - The primitive's declared parameters.
60
+ * @param warn - Diagnostic sink; called once per rejected or unknown parameter.
61
+ * @returns Custom property names mapped to validated values.
62
+ * @complexity O(p * n) time in parameter count and value length; O(p) space.
63
+ * @overallScore 100
64
+ */
65
+ export declare function resolveParams(authored: Record<string, string>, schema: ParameterSchema, warn: (message: string) => void): ResolvedParams;
@@ -0,0 +1,22 @@
1
+ import type { ParsedValue } from './types.js';
2
+ /**
3
+ * Split on a delimiter, ignoring delimiters nested inside parentheses or quotes.
4
+ *
5
+ * @param input - Raw attribute text.
6
+ * @param delimiter - `','` for effect segments, `' '` for tokens within a segment.
7
+ * @param warnings - Sink for a diagnostic when a quote or `(` is never closed. Optional so the
8
+ * two-argument call every existing caller and test uses keeps working unchanged.
9
+ * @returns Trimmed, non-empty parts.
10
+ * @complexity O(n) time in input length; O(n) space for the parts.
11
+ * @overallScore 100
12
+ */
13
+ export declare function splitTopLevel(input: string, delimiter: ',' | ' ', warnings?: string[]): string[];
14
+ /**
15
+ * Parse a `data-kui` attribute value.
16
+ *
17
+ * @param input - Raw attribute text; empty or whitespace yields an empty spec list.
18
+ * @returns Effect specs plus any hoisted element-scoped settings and warnings.
19
+ * @complexity O(n) time in input length; O(e) space in the number of effect segments.
20
+ * @overallScore 100
21
+ */
22
+ export declare function parse(input: string): ParsedValue;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * SVG path interpolation.
3
+ *
4
+ * Two `d` strings cannot be interpolated directly: they differ in command type and count, and
5
+ * `1px` of `C` is not `1px` of `L`. The fix is normalisation — convert everything to absolute
6
+ * cubic segments, then split the longer path's segments until both have the same count. After
7
+ * that, morphing is per-number lerp.
8
+ *
9
+ * Scope is deliberately bounded. `M L H V C Z` are supported; `A S Q T` are not, and a path using
10
+ * them reports a reason rather than producing a plausible-looking wrong shape. Owning a complete
11
+ * SVG path engine is not worth it for two effect names.
12
+ */
13
+ export interface Point {
14
+ x: number;
15
+ y: number;
16
+ }
17
+ /** One cubic segment: start point, two controls, end point. */
18
+ export interface Cubic {
19
+ from: Point;
20
+ c1: Point;
21
+ c2: Point;
22
+ to: Point;
23
+ }
24
+ export interface ParseResult {
25
+ segments: Cubic[];
26
+ /** Present when the path could not be normalised; the caller should warn and not morph. */
27
+ reason?: string;
28
+ }
29
+ /**
30
+ * Parse a path's `d` attribute into absolute cubic segments.
31
+ *
32
+ * @param d - The `d` attribute value.
33
+ * @returns Segments, or a `reason` when the path uses unsupported commands.
34
+ * @complexity O(n) time in the length of `d`; O(s) space in segment count.
35
+ * @overallScore 100
36
+ */
37
+ export declare function parsePath(d: string): ParseResult;
38
+ /**
39
+ * Split a cubic at `t` using de Casteljau, preserving the exact curve.
40
+ *
41
+ * Splitting rather than padding is what keeps a normalised path visually identical to the
42
+ * original — a duplicated zero-length segment would create a visible kink under interpolation.
43
+ *
44
+ * @complexity O(1) time and space.
45
+ * @overallScore 100
46
+ */
47
+ export declare function splitCubic(segment: Cubic, t: number): [Cubic, Cubic];
48
+ /**
49
+ * Grow a segment list to `target` segments by repeatedly halving the longest one.
50
+ *
51
+ * @param segments - Source segments; not mutated.
52
+ * @param target - Desired count, which must be at least the source count.
53
+ * @returns A list of exactly `target` segments describing the same curve.
54
+ * @complexity O((target - n) * n) time; O(target) space. Both paths are parsed once per morph
55
+ * setup, not per frame, so the quadratic term never reaches the frame budget.
56
+ * @overallScore 100
57
+ */
58
+ export declare function normaliseCount(segments: Cubic[], target: number): Cubic[];
59
+ /**
60
+ * Serialise cubic segments back to a `d` string.
61
+ *
62
+ * @complexity O(n) time and space in segment count.
63
+ * @overallScore 100
64
+ */
65
+ export declare function toPathData(segments: Cubic[]): string;
66
+ export interface Morph {
67
+ /** Path data at `t` in [0, 1]. */
68
+ at(t: number): string;
69
+ segmentCount: number;
70
+ }
71
+ /**
72
+ * Build an interpolator between two path strings.
73
+ *
74
+ * @param fromPath - Starting `d`.
75
+ * @param toPath - Ending `d`.
76
+ * @returns A morph, or a `reason` when either path cannot be normalised.
77
+ * @complexity O(n log n)-ish setup in segment count; O(n) per `at` call.
78
+ * @overallScore 100
79
+ */
80
+ export declare function createMorph(fromPath: string, toPath: string): {
81
+ morph?: Morph;
82
+ reason?: string;
83
+ };
@@ -0,0 +1,48 @@
1
+ import type { Animator } from './animator.js';
2
+ export type Target = string | Element | Iterable<Element>;
3
+ export interface PlayOptions {
4
+ duration?: string | number;
5
+ delay?: string | number;
6
+ ease?: string;
7
+ stagger?: string | number;
8
+ /** Any effect parameter, e.g. `{ distance: '40px' }`. */
9
+ [param: string]: string | number | undefined;
10
+ }
11
+ /**
12
+ * A handle, not a bare promise. `finished` resolving and the animation being cancellable are
13
+ * different concerns, and a selection-level `.stop()` cannot express which run it stops.
14
+ */
15
+ export interface PlaybackHandle {
16
+ readonly elements: Element[];
17
+ /** Resolves when every selected element finishes. Resolves (never rejects) on cancel. */
18
+ readonly finished: Promise<void>;
19
+ cancel(): void;
20
+ finish(): void;
21
+ }
22
+ export declare function resolveTargets(target: Target, root: ParentNode): Element[];
23
+ /**
24
+ * Options object → the same attribute string an author would write. One execution path.
25
+ *
26
+ * @complexity O(n) time in the number of options; O(n) space for the parts.
27
+ * @overallScore 100
28
+ */
29
+ export declare function toAttributeValue(effect: string, options?: PlayOptions): string;
30
+ export interface PlayRequest {
31
+ animator: Animator;
32
+ root: ParentNode;
33
+ target: Target;
34
+ effect: string;
35
+ }
36
+ /**
37
+ * Play an effect on a selection, returning a handle rather than a bare promise.
38
+ *
39
+ * Options are compiled into the same attribute string an author would write, so the declarative
40
+ * and programmatic surfaces share one execution path instead of drifting apart.
41
+ *
42
+ * @param request - Animator, root, target selection, and effect name.
43
+ * @param options - Timing and effect parameters.
44
+ * @returns A handle exposing `finished`, `cancel`, and `finish`.
45
+ * @complexity O(n) time in the number of selected elements; O(n) space.
46
+ * @overallScore 100
47
+ */
48
+ export declare function play(request: PlayRequest, options?: PlayOptions): PlaybackHandle;
@@ -0,0 +1,38 @@
1
+ import type { Preset, Primitive } from './types.js';
2
+ export interface ResolvedEffect {
3
+ preset: Preset;
4
+ primitive: Primitive;
5
+ }
6
+ /**
7
+ * Name → primitive alias table.
8
+ *
9
+ * The catalog's ~237 names come from 29 primitives; 48 of them are one primitive with different
10
+ * parameter defaults. Presets are therefore data rows, not code, and adding a name in a later
11
+ * release costs a table entry. See docs/catalog.md.
12
+ */
13
+ export declare class Registry {
14
+ private primitives;
15
+ private presets;
16
+ /** Sorted-name key → preset that renders that combination as one tested keyframe. */
17
+ private combos;
18
+ registerPrimitive(primitive: Primitive): this;
19
+ registerPreset(preset: Preset): this;
20
+ registerPresets(presets: Preset[]): this;
21
+ registerPrimitives(primitives: Primitive[]): this;
22
+ /**
23
+ * Declare that a set of effect names has a purpose-built single-keyframe implementation.
24
+ * Checked before channel conflict analysis, so `fade-up` + `blur-in` can resolve to the
25
+ * tested `fade-blur-up` rather than being rejected for both writing `opacity`.
26
+ */
27
+ registerCombo(names: string[], presetName: string): this;
28
+ resolve(name: string): ResolvedEffect | undefined;
29
+ findCombo(names: string[]): ResolvedEffect | undefined;
30
+ has(name: string): boolean;
31
+ /** All registered effect names, for docs generation and dev-mode "did you mean" hints. */
32
+ names(): string[];
33
+ getPrimitive(id: string): Primitive | undefined;
34
+ }
35
+ /** Custom property a primitive's timing parameter writes to. */
36
+ export declare function timingProperty(primitiveId: string, name: string): string;
37
+ /** Levenshtein-lite suggestion for unknown effect names. Dev-mode ergonomics only. */
38
+ export declare function suggest(name: string, candidates: string[]): string | undefined;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Diagnostic sink.
3
+ *
4
+ * Injected rather than calling `console` directly so warnings are assertable in tests and
5
+ * silenceable in production without a build-time flag.
6
+ */
7
+ export interface Reporter {
8
+ warn(message: string, subject?: unknown): void;
9
+ }
10
+ /**
11
+ * Reporter that writes to the console with a library prefix.
12
+ *
13
+ * @returns A reporter suitable for development builds.
14
+ * @complexity O(1) time, O(1) space.
15
+ * @overallScore 100
16
+ */
17
+ export declare function consoleReporter(): Reporter;
18
+ /**
19
+ * Reporter that discards everything. The production default.
20
+ *
21
+ * @complexity O(1) time, O(1) space.
22
+ * @overallScore 100
23
+ */
24
+ export declare function silentReporter(): Reporter;
25
+ export interface CollectingReporter extends Reporter {
26
+ readonly messages: string[];
27
+ }
28
+ /**
29
+ * Reporter that records messages for assertion.
30
+ *
31
+ * @returns A reporter whose `messages` array accumulates every warning in order.
32
+ * @complexity O(1) amortised per warning; O(n) space in the number of warnings.
33
+ * @overallScore 100
34
+ */
35
+ export declare function collectingReporter(): CollectingReporter;