@rootnative/inertia 0.0.5 → 0.0.6

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 (37) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +1 -1
  3. package/dist/{chunk-RIVVBABB.mjs → chunk-2ICQLWH2.mjs} +81 -34
  4. package/dist/{chunk-27U6766G.js → chunk-5MGWHOSV.js} +2 -2
  5. package/dist/{chunk-VQ5D35UA.js → chunk-767UKZXG.js} +81 -33
  6. package/dist/{chunk-27MKBTSG.mjs → chunk-BAAQI37F.mjs} +1 -1
  7. package/dist/{chunk-OAUWLPQH.mjs → chunk-C3EDC5ZW.mjs} +1 -1
  8. package/dist/{chunk-GTJ6VAH5.mjs → chunk-CMHVF6F4.mjs} +1 -1
  9. package/dist/{chunk-COEJVWRZ.mjs → chunk-F7LJX56B.mjs} +1 -1
  10. package/dist/chunk-FWWVHLXB.js +8 -0
  11. package/dist/chunk-K5SI6VXS.js +8 -0
  12. package/dist/{chunk-XSK5MNUH.mjs → chunk-PFPO7DX2.mjs} +1 -1
  13. package/dist/chunk-STARIT6W.js +8 -0
  14. package/dist/{chunk-QQVDZKSD.js → chunk-X7B5WR5A.js} +2 -2
  15. package/dist/index.d.mts +29 -1
  16. package/dist/index.d.ts +29 -1
  17. package/dist/index.js +26 -22
  18. package/dist/index.mjs +12 -12
  19. package/dist/motion/Image.js +3 -3
  20. package/dist/motion/Image.mjs +2 -2
  21. package/dist/motion/Pressable.js +3 -3
  22. package/dist/motion/Pressable.mjs +2 -2
  23. package/dist/motion/ScrollView.js +3 -3
  24. package/dist/motion/ScrollView.mjs +2 -2
  25. package/dist/motion/Text.js +3 -3
  26. package/dist/motion/Text.mjs +2 -2
  27. package/dist/motion/View.js +3 -3
  28. package/dist/motion/View.mjs +2 -2
  29. package/llms.txt +1 -0
  30. package/package.json +1 -1
  31. package/src/index.ts +4 -0
  32. package/src/internal/boxShadow.ts +71 -9
  33. package/src/internal/color.ts +70 -0
  34. package/src/motion/createMotionComponent.tsx +93 -47
  35. package/dist/chunk-67GHRCF6.js +0 -8
  36. package/dist/chunk-GPOMFIIU.js +0 -8
  37. package/dist/chunk-K63LXGKS.js +0 -8
package/CHANGELOG.md CHANGED
@@ -4,6 +4,40 @@ All notable changes to `@rootnative/inertia` are documented here. The format fol
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.0.6] - 2026-08-08
8
+
9
+ **Defect release: two animations that never ran.** Both trace to one assumption about what Reanimated accepts as an animatable value, and both were hidden by the same thing — `type: 'timing'` snaps to its target when the duration elapses, so a total interpolation failure still produced the right end state. `animate={{ boxShadow }}` was inert under the default spring; every colour key was inert when it rested at its default, which is the far more common path. Neither is caught by a mocked test suite, so this release also adds a test file that runs Reanimated's real drivers.
10
+
11
+ ### Fixed
12
+
13
+ - **`animate={{ boxShadow }}` now animates under `type: 'spring'`** — the library default, and the documented recommendation, so `animate={{ boxShadow }}` with no `transition` prop at all was the broken path. The key worked under `type: 'timing'` and was inert under spring, from its introduction in `0.0.4` until now.
14
+
15
+ The cause was the shape of the value handed to Reanimated. Its animation drivers walk a structured value by dispatching on each leaf's runtime shape, but the two container branches are **not symmetrical** (`animation/util.ts`): `objectOnStart` re-assigns the decorated `onStart` onto each child, so a child that is itself an object or a colour is dispatched again, recursively — while `arrayOnStart` does not, handing every element straight to the scalar spring/timing maths. `boxShadow`'s payload was an array of layer objects, so each layer was evaluated as `object - object` → `NaN`.
16
+
17
+ Why only spring broke: `withTiming` snaps to its target once the duration elapses, whatever the interpolation produced along the way, so the shadow still arrived and the fault stayed hidden. `withSpring` decides it has settled via `isAnimationTerminatingCalculation`, and every comparison against `NaN` is false — so the animation never finished, the shared value held `'[object Object]NaN'` indefinitely, `onAnimationEnd` never fired, and the frame loop never stopped.
18
+
19
+ The payload is now keyed by index (`{ 0: layer, 1: layer }`), which routes it down the object branch where the recursion works and each layer's colour reaches the RGBA-channel path. The change is internal: `animate={{ boxShadow }}` accepts the same CSS string and `BoxShadowValue[]` forms, endpoint padding is unchanged, and `onAnimationEnd`'s `value` is still reported as a layer list.
20
+
21
+ - **Colour keys can now animate away from their resting default.** `'transparent'` is the one CSS colour name Reanimated's `isColor()` rejects — its colour table maps the keyword to `undefined` while every other name maps to a packed integer. A value that fails that gate but is still a string falls to the prefix-number-suffix branch built for values like `'100%'`, producing `'transparentNaN'` and, under spring, never settling.
22
+
23
+ `'transparent'` was Inertia's resting default for `backgroundColor`, `borderColor`, `color`, `tintColor`, and `shadowColor`, so `<Motion.View animate={{ backgroundColor: '#4f46e5' }} />` on an element with no static colour and no `initial` never animated. All five defaults are now `'rgba(0, 0, 0, 0)'` — the identical colour, recognised by the gate — and the keyword is rewritten to it wherever it would enter a colour slot: `initial`, `animate` (including keyframe sequences and `{ to }` steps), and the static `style` a never-driven key rests at.
24
+
25
+ Only values handed to `withSpring` / `withTiming` are affected. `interpolateColor` parses the keyword correctly, so the `gesture` cascade, the `layoutId` style carry, and `useShadow` were never affected and keep the consumer's own spelling.
26
+
27
+ **Visible difference:** a colour key resting at its default now renders `'rgba(0, 0, 0, 0)'` where it used to render `'transparent'`. Same colour, but a snapshot test asserting the literal string will need updating.
28
+
29
+ - **`boxShadow`'s transparent padding layer** — added when the two endpoints have different layer counts — used the same keyword, so a single padding layer was enough to hang a whole shadow animation. It is now `'rgba(0, 0, 0, 0)'`.
30
+
31
+ ### Added
32
+
33
+ - **`TRANSPARENT`** — the colour a custom animated component should seed a colour shared value with when it has no other source. Exported alongside `resolveTransition` and the other building blocks for custom components, because the obvious choice (`'transparent'`) is the one spelling Reanimated cannot animate away from, and nothing about the failure points at the cause.
34
+
35
+ **Bundle cost: +0.13 kB (+1.5%) per primitive subpath**, 8.92 → 9.05 kB brotlied, +0.18 kB on the root entry (12.10 → 12.28 kB). No `size-limit` cap moved. The payload reshape is close to free — one index-keying pass on the JS thread and one loop in the worklet, replacing an array that crossed over by reference — and most of the delta is the colour normalization, which has to run on four separate paths into a slot.
36
+
37
+ ### Internal
38
+
39
+ - **`reanimated-drivers.test.ts` runs Reanimated's real animation drivers**, imported by deep path, instead of the static mock the rest of the suite uses. Both defects above were invisible to that mock — `withSpring` and `withTiming` are the identity function there, so the payload Inertia produced looked correct in every assertion while being unanimatable in fact. The new file drives Reanimated's own `onStart` / `onFrame` protocol (the one its `valueSetter` runs) and asserts that leaves converge, that a spring settles, and that mid-flight frames carry numbers. It also pins the two upstream behaviours the fixes work around, so a Reanimated upgrade that changes either fails loudly rather than silently leaving dead workarounds behind.
40
+
7
41
  ## [0.0.5] - 2026-07-31
8
42
 
9
43
  **Correctness release for the `animate` type surface.** The 40 layout and text-metric style keys that typechecked but were silently dropped at runtime now either animate or fail to compile. No API removals; the type narrowing is technically breaking for code that passed a never-driven key, but that code was already a no-op.
@@ -191,7 +225,8 @@ Initial alpha publish. The full initial surface is in place; APIs are still subj
191
225
  - SVG path morphing, gradient interpolation, and shared-element transitions across screens are out of scope until `0.2.x` / `1.x` per the roadmap.
192
226
  - `react-native-gesture-handler` integration (drag, pan, swipe sub-states) lands in `0.2` via the optional `@rootnative/inertia-gestures` adapter.
193
227
 
194
- [unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.5...HEAD
228
+ [unreleased]: https://github.com/rootnative/inertia/compare/core+gestures+gradients+svg@0.0.6...HEAD
229
+ [0.0.6]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.6
195
230
  [0.0.5]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.5
196
231
  [0.0.4]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.4
197
232
  [0.0.3]: https://github.com/rootnative/inertia/releases/tag/core+gestures+gradients+svg@0.0.3
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Declarative animation primitives for React Native, built as a thin wrapper around [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/). Inspired by Framer Motion (web) and react-spring (cross-platform).
8
8
 
9
- > **Status:** `0.0.5` — stable. Pre-`1.0.0` minor versions may break — see the root [README](https://github.com/rootnative/inertia#versioning--release).
9
+ > **Status:** `0.0.6` — stable. Pre-`1.0.0` minor versions may break — see the root [README](https://github.com/rootnative/inertia#versioning--release).
10
10
 
11
11
  ## Install
12
12
 
@@ -5,6 +5,30 @@ import { jsx, Fragment } from 'react/jsx-runtime';
5
5
  import Animated, { useSharedValue, cancelAnimation, useAnimatedStyle, interpolateColor, reanimatedVersion, runOnJS, LinearTransition, withSequence, withTiming, withSpring } from 'react-native-reanimated';
6
6
  import { StyleSheet } from 'react-native';
7
7
 
8
+ // src/internal/color.ts
9
+ var TRANSPARENT = "rgba(0, 0, 0, 0)";
10
+ function normalizeAnimatableColor(value) {
11
+ return value === "transparent" ? TRANSPARENT : value;
12
+ }
13
+ function normalizeAnimatableColorTarget(target) {
14
+ if (typeof target === "string") {
15
+ return normalizeAnimatableColor(target);
16
+ }
17
+ if (!Array.isArray(target)) return target;
18
+ let changed = false;
19
+ const next = target.map((step) => {
20
+ if (step === "transparent") {
21
+ changed = true;
22
+ return TRANSPARENT;
23
+ }
24
+ if (step !== null && typeof step === "object" && step.to === "transparent") {
25
+ changed = true;
26
+ return { ...step, to: TRANSPARENT };
27
+ }
28
+ return step;
29
+ });
30
+ return changed ? next : target;
31
+ }
8
32
  var PresenceContext = createContext(null);
9
33
  function usePresence() {
10
34
  return useContext(PresenceContext);
@@ -244,7 +268,7 @@ function invisibleLayer(inset) {
244
268
  offsetY: 0,
245
269
  blurRadius: 0,
246
270
  spreadDistance: 0,
247
- color: "transparent",
271
+ color: TRANSPARENT,
248
272
  inset
249
273
  };
250
274
  }
@@ -260,8 +284,8 @@ function coerceLength(value, field) {
260
284
  return parseFloat(trimmed);
261
285
  }
262
286
  function strip(layer) {
263
- const { inset: _inset, ...rest } = layer;
264
- return rest;
287
+ const { inset: _inset, color, ...rest } = layer;
288
+ return { ...rest, color: normalizeAnimatableColor(color) };
265
289
  }
266
290
  function markInset(insets, i, count) {
267
291
  const list = insets ?? new Array(count).fill(false);
@@ -298,6 +322,17 @@ function prepareBoxShadowAnimation(current, target) {
298
322
  }
299
323
  return { from, to, insets };
300
324
  }
325
+ function layersToPayload(layers) {
326
+ const payload = {};
327
+ for (let i = 0; i < layers.length; i++) payload[i] = layers[i];
328
+ return payload;
329
+ }
330
+ function payloadToLayers(payload) {
331
+ "worklet";
332
+ const layers = [];
333
+ for (let i = 0; payload[i] !== void 0; i++) layers.push(payload[i]);
334
+ return layers;
335
+ }
301
336
  function resolveLayoutTransition(layout) {
302
337
  if (!layout) return void 0;
303
338
  const cfg = layout === true ? { type: "spring" } : layout;
@@ -697,7 +732,7 @@ var COLOR_KEY_SET = new Set(COLOR_KEYS);
697
732
  var SHADOW_OFFSET_KEY_SET = new Set(SHADOW_OFFSET_KEYS);
698
733
  var STRUCTURED_KEY_SET = new Set(STRUCTURED_KEYS);
699
734
  var SHARED_STYLE_KEY_SET = new Set(SHARED_STYLE_KEYS);
700
- var NO_BOX_SHADOW = Object.freeze([]);
735
+ var NO_BOX_SHADOW = Object.freeze({});
701
736
  var GESTURE_LAYER_NAMES = [
702
737
  "hovered",
703
738
  "focused",
@@ -770,15 +805,21 @@ var DEFAULT_RESTING = {
770
805
  gap: 0,
771
806
  rowGap: 0,
772
807
  columnGap: 0,
773
- // 'transparent' is the only safe universal default for colors: it works as
774
- // an initial seed for any color animation (no jarring opaque flash on mount
775
- // when `initial` is omitted) and rgba(0,0,0,0) interpolates cleanly into
776
- // any opaque target via Reanimated's color util.
777
- backgroundColor: "transparent",
778
- borderColor: "transparent",
779
- color: "transparent",
780
- tintColor: "transparent",
781
- shadowColor: "transparent",
808
+ // Fully transparent black is the only safe universal default for colors: it
809
+ // works as an initial seed for any color animation (no jarring opaque flash
810
+ // on mount when `initial` is omitted) and interpolates cleanly into any
811
+ // opaque target.
812
+ //
813
+ // Spelled `rgba(0, 0, 0, 0)` and NOT `'transparent'`, which is not optional.
814
+ // Reanimated's color-name table maps `transparent` to `undefined`, so it is
815
+ // the one named color `isColor()` rejects — a slot resting at the keyword
816
+ // takes the prefix-number-suffix branch on its next animation and produces
817
+ // `NaN` instead of a color. See `TRANSPARENT` in `internal/boxShadow.ts`.
818
+ backgroundColor: TRANSPARENT,
819
+ borderColor: TRANSPARENT,
820
+ color: TRANSPARENT,
821
+ tintColor: TRANSPARENT,
822
+ shadowColor: TRANSPARENT,
782
823
  shadowOffsetWidth: 0,
783
824
  shadowOffsetHeight: 0,
784
825
  boxShadow: NO_BOX_SHADOW
@@ -965,25 +1006,28 @@ function createMotionComponent(Component) {
965
1006
  const raw = flat.boxShadow;
966
1007
  if (raw !== void 0) {
967
1008
  styleRestingShadow = normalizeBoxShadow(raw);
968
- styleResting.boxShadow = styleRestingShadow.layers;
1009
+ styleResting.boxShadow = layersToPayload(
1010
+ styleRestingShadow.layers
1011
+ );
969
1012
  }
970
1013
  continue;
971
1014
  }
972
1015
  const v = styleValueFor(flat, key);
973
- if (v !== void 0) styleResting[key] = v;
1016
+ if (v !== void 0) {
1017
+ styleResting[key] = COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(v) : v;
1018
+ }
974
1019
  }
975
1020
  }
976
1021
  }
977
1022
  const boxShadowSeedRef = useRef(null);
978
1023
  if (boxShadowSeedRef.current === null) {
979
1024
  const source = initial === false ? animateRecord.boxShadow : initialRecord?.boxShadow ?? animateRecord.boxShadow;
980
- boxShadowSeedRef.current = source !== void 0 ? normalizeBoxShadow(source) : styleRestingShadow ?? {
981
- layers: [...NO_BOX_SHADOW],
982
- insets: null
983
- };
1025
+ boxShadowSeedRef.current = source !== void 0 ? normalizeBoxShadow(source) : styleRestingShadow ?? { layers: [], insets: null };
984
1026
  }
985
1027
  const sharedValues = useAnimatableSharedValues((key) => {
986
- if (key === "boxShadow") return boxShadowSeedRef.current.layers;
1028
+ if (key === "boxShadow") {
1029
+ return layersToPayload(boxShadowSeedRef.current.layers);
1030
+ }
987
1031
  if (SHADOW_OFFSET_KEY_SET.has(key)) {
988
1032
  const axis = shadowOffsetAxisFor(key);
989
1033
  if (initial === false) {
@@ -996,9 +1040,11 @@ function createMotionComponent(Component) {
996
1040
  }
997
1041
  if (initial === false) {
998
1042
  const a = animateRecord[key];
999
- return restValue(a) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1043
+ const seed2 = restValue(a) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1044
+ return COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(seed2) : seed2;
1000
1045
  }
1001
- return initialRecord?.[key] ?? restValue(animateRecord[key]) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1046
+ const seed = initialRecord?.[key] ?? restValue(animateRecord[key]) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1047
+ return COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(seed) : seed;
1002
1048
  });
1003
1049
  const boxShadowInsets = useSharedValue(
1004
1050
  boxShadowSeedRef.current.insets
@@ -1111,7 +1157,7 @@ function createMotionComponent(Component) {
1111
1157
  TRANSFORM_KEY_SET.has(key) ? transformGroup : void 0
1112
1158
  );
1113
1159
  sharedValues[key].value = resolveAnimatableValue(
1114
- target,
1160
+ COLOR_KEY_SET.has(key) ? normalizeAnimatableColorTarget(target) : target,
1115
1161
  cfg,
1116
1162
  factory
1117
1163
  );
@@ -1248,14 +1294,14 @@ function createMotionComponent(Component) {
1248
1294
  out.shadowOffset = { width: shadowOffsetW, height: shadowOffsetH };
1249
1295
  }
1250
1296
  if (hasBoxShadow) {
1251
- const layers2 = sharedValues.boxShadow.value;
1297
+ const payload = sharedValues.boxShadow.value;
1252
1298
  const insets = boxShadowInsets.value;
1253
1299
  if (insets === null) {
1254
- out.boxShadow = layers2;
1300
+ out.boxShadow = payloadToLayers(payload);
1255
1301
  } else {
1256
1302
  const withInset = [];
1257
- for (let i = 0; i < layers2.length; i++) {
1258
- withInset.push({ ...layers2[i], inset: insets[i] });
1303
+ for (let i = 0; payload[i] !== void 0; i++) {
1304
+ withInset.push({ ...payload[i], inset: insets[i] });
1259
1305
  }
1260
1306
  out.boxShadow = withInset;
1261
1307
  }
@@ -1371,6 +1417,7 @@ function makeKeyCallbackFactory(key, sharedValue, target, onAnimationEndRef, met
1371
1417
  }
1372
1418
  const reportedIteration = state.iteration;
1373
1419
  if (phase === "sequence" || phase === "repeat") state.iteration++;
1420
+ const reportedValue = key === "boxShadow" && value !== void 0 ? payloadToLayers(value) : value;
1374
1421
  const fn = onAnimationEndRef.current;
1375
1422
  if (fn) {
1376
1423
  if (isTransformKey && transformGroup && phase === "animation") {
@@ -1379,7 +1426,7 @@ function makeKeyCallbackFactory(key, sharedValue, target, onAnimationEndRef, met
1379
1426
  fn({
1380
1427
  key: "transform",
1381
1428
  finished,
1382
- value,
1429
+ value: reportedValue,
1383
1430
  target,
1384
1431
  phase,
1385
1432
  step,
@@ -1390,7 +1437,7 @@ function makeKeyCallbackFactory(key, sharedValue, target, onAnimationEndRef, met
1390
1437
  fn({
1391
1438
  key,
1392
1439
  finished,
1393
- value,
1440
+ value: reportedValue,
1394
1441
  target,
1395
1442
  phase,
1396
1443
  step,
@@ -1415,16 +1462,16 @@ function shadowOffsetAxisValue(source, axis) {
1415
1462
  return source?.[axis];
1416
1463
  }
1417
1464
  function driveBoxShadow(slot, insetSlot, target, cfg, factory) {
1418
- const currentLayers = Array.isArray(slot.value) ? slot.value : NO_BOX_SHADOW;
1465
+ const currentLayers = payloadToLayers(slot.value);
1419
1466
  const { from, to, insets } = prepareBoxShadowAnimation(
1420
- { layers: [...currentLayers], insets: insetSlot.value },
1467
+ { layers: currentLayers, insets: insetSlot.value },
1421
1468
  target
1422
1469
  );
1423
1470
  insetSlot.value = insets;
1424
- if (from.length !== currentLayers.length) slot.value = from;
1471
+ if (from.length !== currentLayers.length) slot.value = layersToPayload(from);
1425
1472
  slot.value = resolveTransition(
1426
1473
  cfg,
1427
- to,
1474
+ layersToPayload(to),
1428
1475
  factory?.("animation", void 0)
1429
1476
  );
1430
1477
  }
@@ -1642,4 +1689,4 @@ function compose(user, ours) {
1642
1689
  };
1643
1690
  }
1644
1691
 
1645
- export { Presence, createMotionComponent, pairBoxShadowLayers, resolveBoxShadowInput, usePresence };
1692
+ export { Presence, TRANSPARENT, createMotionComponent, pairBoxShadowLayers, resolveBoxShadowInput, usePresence };
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkVQ5D35UA_js = require('./chunk-VQ5D35UA.js');
3
+ var chunk767UKZXG_js = require('./chunk-767UKZXG.js');
4
4
  var reactNative = require('react-native');
5
5
 
6
- var MotionPressable = chunkVQ5D35UA_js.createMotionComponent(reactNative.Pressable);
6
+ var MotionPressable = chunk767UKZXG_js.createMotionComponent(reactNative.Pressable);
7
7
 
8
8
  exports.MotionPressable = MotionPressable;
@@ -11,6 +11,30 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
12
12
  var Animated__default = /*#__PURE__*/_interopDefault(Animated);
13
13
 
14
+ // src/internal/color.ts
15
+ var TRANSPARENT = "rgba(0, 0, 0, 0)";
16
+ function normalizeAnimatableColor(value) {
17
+ return value === "transparent" ? TRANSPARENT : value;
18
+ }
19
+ function normalizeAnimatableColorTarget(target) {
20
+ if (typeof target === "string") {
21
+ return normalizeAnimatableColor(target);
22
+ }
23
+ if (!Array.isArray(target)) return target;
24
+ let changed = false;
25
+ const next = target.map((step) => {
26
+ if (step === "transparent") {
27
+ changed = true;
28
+ return TRANSPARENT;
29
+ }
30
+ if (step !== null && typeof step === "object" && step.to === "transparent") {
31
+ changed = true;
32
+ return { ...step, to: TRANSPARENT };
33
+ }
34
+ return step;
35
+ });
36
+ return changed ? next : target;
37
+ }
14
38
  var PresenceContext = react.createContext(null);
15
39
  function usePresence() {
16
40
  return react.useContext(PresenceContext);
@@ -250,7 +274,7 @@ function invisibleLayer(inset) {
250
274
  offsetY: 0,
251
275
  blurRadius: 0,
252
276
  spreadDistance: 0,
253
- color: "transparent",
277
+ color: TRANSPARENT,
254
278
  inset
255
279
  };
256
280
  }
@@ -266,8 +290,8 @@ function coerceLength(value, field) {
266
290
  return parseFloat(trimmed);
267
291
  }
268
292
  function strip(layer) {
269
- const { inset: _inset, ...rest } = layer;
270
- return rest;
293
+ const { inset: _inset, color, ...rest } = layer;
294
+ return { ...rest, color: normalizeAnimatableColor(color) };
271
295
  }
272
296
  function markInset(insets, i, count) {
273
297
  const list = insets ?? new Array(count).fill(false);
@@ -304,6 +328,17 @@ function prepareBoxShadowAnimation(current, target) {
304
328
  }
305
329
  return { from, to, insets };
306
330
  }
331
+ function layersToPayload(layers) {
332
+ const payload = {};
333
+ for (let i = 0; i < layers.length; i++) payload[i] = layers[i];
334
+ return payload;
335
+ }
336
+ function payloadToLayers(payload) {
337
+ "worklet";
338
+ const layers = [];
339
+ for (let i = 0; payload[i] !== void 0; i++) layers.push(payload[i]);
340
+ return layers;
341
+ }
307
342
  function resolveLayoutTransition(layout) {
308
343
  if (!layout) return void 0;
309
344
  const cfg = layout === true ? { type: "spring" } : layout;
@@ -703,7 +738,7 @@ var COLOR_KEY_SET = new Set(COLOR_KEYS);
703
738
  var SHADOW_OFFSET_KEY_SET = new Set(SHADOW_OFFSET_KEYS);
704
739
  var STRUCTURED_KEY_SET = new Set(STRUCTURED_KEYS);
705
740
  var SHARED_STYLE_KEY_SET = new Set(SHARED_STYLE_KEYS);
706
- var NO_BOX_SHADOW = Object.freeze([]);
741
+ var NO_BOX_SHADOW = Object.freeze({});
707
742
  var GESTURE_LAYER_NAMES = [
708
743
  "hovered",
709
744
  "focused",
@@ -776,15 +811,21 @@ var DEFAULT_RESTING = {
776
811
  gap: 0,
777
812
  rowGap: 0,
778
813
  columnGap: 0,
779
- // 'transparent' is the only safe universal default for colors: it works as
780
- // an initial seed for any color animation (no jarring opaque flash on mount
781
- // when `initial` is omitted) and rgba(0,0,0,0) interpolates cleanly into
782
- // any opaque target via Reanimated's color util.
783
- backgroundColor: "transparent",
784
- borderColor: "transparent",
785
- color: "transparent",
786
- tintColor: "transparent",
787
- shadowColor: "transparent",
814
+ // Fully transparent black is the only safe universal default for colors: it
815
+ // works as an initial seed for any color animation (no jarring opaque flash
816
+ // on mount when `initial` is omitted) and interpolates cleanly into any
817
+ // opaque target.
818
+ //
819
+ // Spelled `rgba(0, 0, 0, 0)` and NOT `'transparent'`, which is not optional.
820
+ // Reanimated's color-name table maps `transparent` to `undefined`, so it is
821
+ // the one named color `isColor()` rejects — a slot resting at the keyword
822
+ // takes the prefix-number-suffix branch on its next animation and produces
823
+ // `NaN` instead of a color. See `TRANSPARENT` in `internal/boxShadow.ts`.
824
+ backgroundColor: TRANSPARENT,
825
+ borderColor: TRANSPARENT,
826
+ color: TRANSPARENT,
827
+ tintColor: TRANSPARENT,
828
+ shadowColor: TRANSPARENT,
788
829
  shadowOffsetWidth: 0,
789
830
  shadowOffsetHeight: 0,
790
831
  boxShadow: NO_BOX_SHADOW
@@ -971,25 +1012,28 @@ function createMotionComponent(Component) {
971
1012
  const raw = flat.boxShadow;
972
1013
  if (raw !== void 0) {
973
1014
  styleRestingShadow = normalizeBoxShadow(raw);
974
- styleResting.boxShadow = styleRestingShadow.layers;
1015
+ styleResting.boxShadow = layersToPayload(
1016
+ styleRestingShadow.layers
1017
+ );
975
1018
  }
976
1019
  continue;
977
1020
  }
978
1021
  const v = styleValueFor(flat, key);
979
- if (v !== void 0) styleResting[key] = v;
1022
+ if (v !== void 0) {
1023
+ styleResting[key] = COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(v) : v;
1024
+ }
980
1025
  }
981
1026
  }
982
1027
  }
983
1028
  const boxShadowSeedRef = react.useRef(null);
984
1029
  if (boxShadowSeedRef.current === null) {
985
1030
  const source = initial === false ? animateRecord.boxShadow : initialRecord?.boxShadow ?? animateRecord.boxShadow;
986
- boxShadowSeedRef.current = source !== void 0 ? normalizeBoxShadow(source) : styleRestingShadow ?? {
987
- layers: [...NO_BOX_SHADOW],
988
- insets: null
989
- };
1031
+ boxShadowSeedRef.current = source !== void 0 ? normalizeBoxShadow(source) : styleRestingShadow ?? { layers: [], insets: null };
990
1032
  }
991
1033
  const sharedValues = useAnimatableSharedValues((key) => {
992
- if (key === "boxShadow") return boxShadowSeedRef.current.layers;
1034
+ if (key === "boxShadow") {
1035
+ return layersToPayload(boxShadowSeedRef.current.layers);
1036
+ }
993
1037
  if (SHADOW_OFFSET_KEY_SET.has(key)) {
994
1038
  const axis = shadowOffsetAxisFor(key);
995
1039
  if (initial === false) {
@@ -1002,9 +1046,11 @@ function createMotionComponent(Component) {
1002
1046
  }
1003
1047
  if (initial === false) {
1004
1048
  const a = animateRecord[key];
1005
- return restValue(a) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1049
+ const seed2 = restValue(a) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1050
+ return COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(seed2) : seed2;
1006
1051
  }
1007
- return initialRecord?.[key] ?? restValue(animateRecord[key]) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1052
+ const seed = initialRecord?.[key] ?? restValue(animateRecord[key]) ?? styleResting[key] ?? DEFAULT_RESTING[key];
1053
+ return COLOR_KEY_SET.has(key) ? normalizeAnimatableColor(seed) : seed;
1008
1054
  });
1009
1055
  const boxShadowInsets = Animated.useSharedValue(
1010
1056
  boxShadowSeedRef.current.insets
@@ -1117,7 +1163,7 @@ function createMotionComponent(Component) {
1117
1163
  TRANSFORM_KEY_SET.has(key) ? transformGroup : void 0
1118
1164
  );
1119
1165
  sharedValues[key].value = chunkPM6CVGXJ_js.resolveAnimatableValue(
1120
- target,
1166
+ COLOR_KEY_SET.has(key) ? normalizeAnimatableColorTarget(target) : target,
1121
1167
  cfg,
1122
1168
  factory
1123
1169
  );
@@ -1254,14 +1300,14 @@ function createMotionComponent(Component) {
1254
1300
  out.shadowOffset = { width: shadowOffsetW, height: shadowOffsetH };
1255
1301
  }
1256
1302
  if (hasBoxShadow) {
1257
- const layers2 = sharedValues.boxShadow.value;
1303
+ const payload = sharedValues.boxShadow.value;
1258
1304
  const insets = boxShadowInsets.value;
1259
1305
  if (insets === null) {
1260
- out.boxShadow = layers2;
1306
+ out.boxShadow = payloadToLayers(payload);
1261
1307
  } else {
1262
1308
  const withInset = [];
1263
- for (let i = 0; i < layers2.length; i++) {
1264
- withInset.push({ ...layers2[i], inset: insets[i] });
1309
+ for (let i = 0; payload[i] !== void 0; i++) {
1310
+ withInset.push({ ...payload[i], inset: insets[i] });
1265
1311
  }
1266
1312
  out.boxShadow = withInset;
1267
1313
  }
@@ -1377,6 +1423,7 @@ function makeKeyCallbackFactory(key, sharedValue, target, onAnimationEndRef, met
1377
1423
  }
1378
1424
  const reportedIteration = state.iteration;
1379
1425
  if (phase === "sequence" || phase === "repeat") state.iteration++;
1426
+ const reportedValue = key === "boxShadow" && value !== void 0 ? payloadToLayers(value) : value;
1380
1427
  const fn = onAnimationEndRef.current;
1381
1428
  if (fn) {
1382
1429
  if (isTransformKey && transformGroup && phase === "animation") {
@@ -1385,7 +1432,7 @@ function makeKeyCallbackFactory(key, sharedValue, target, onAnimationEndRef, met
1385
1432
  fn({
1386
1433
  key: "transform",
1387
1434
  finished,
1388
- value,
1435
+ value: reportedValue,
1389
1436
  target,
1390
1437
  phase,
1391
1438
  step,
@@ -1396,7 +1443,7 @@ function makeKeyCallbackFactory(key, sharedValue, target, onAnimationEndRef, met
1396
1443
  fn({
1397
1444
  key,
1398
1445
  finished,
1399
- value,
1446
+ value: reportedValue,
1400
1447
  target,
1401
1448
  phase,
1402
1449
  step,
@@ -1421,16 +1468,16 @@ function shadowOffsetAxisValue(source, axis) {
1421
1468
  return source?.[axis];
1422
1469
  }
1423
1470
  function driveBoxShadow(slot, insetSlot, target, cfg, factory) {
1424
- const currentLayers = Array.isArray(slot.value) ? slot.value : NO_BOX_SHADOW;
1471
+ const currentLayers = payloadToLayers(slot.value);
1425
1472
  const { from, to, insets } = prepareBoxShadowAnimation(
1426
- { layers: [...currentLayers], insets: insetSlot.value },
1473
+ { layers: currentLayers, insets: insetSlot.value },
1427
1474
  target
1428
1475
  );
1429
1476
  insetSlot.value = insets;
1430
- if (from.length !== currentLayers.length) slot.value = from;
1477
+ if (from.length !== currentLayers.length) slot.value = layersToPayload(from);
1431
1478
  slot.value = chunkPM6CVGXJ_js.resolveTransition(
1432
1479
  cfg,
1433
- to,
1480
+ layersToPayload(to),
1434
1481
  factory?.("animation", void 0)
1435
1482
  );
1436
1483
  }
@@ -1649,6 +1696,7 @@ function compose(user, ours) {
1649
1696
  }
1650
1697
 
1651
1698
  exports.Presence = Presence;
1699
+ exports.TRANSPARENT = TRANSPARENT;
1652
1700
  exports.createMotionComponent = createMotionComponent;
1653
1701
  exports.pairBoxShadowLayers = pairBoxShadowLayers;
1654
1702
  exports.resolveBoxShadowInput = resolveBoxShadowInput;
@@ -1,4 +1,4 @@
1
- import { createMotionComponent } from './chunk-RIVVBABB.mjs';
1
+ import { createMotionComponent } from './chunk-2ICQLWH2.mjs';
2
2
  import { ScrollView } from 'react-native';
3
3
 
4
4
  var MotionScrollView = createMotionComponent(ScrollView);
@@ -1,4 +1,4 @@
1
- import { createMotionComponent } from './chunk-RIVVBABB.mjs';
1
+ import { createMotionComponent } from './chunk-2ICQLWH2.mjs';
2
2
  import { Pressable } from 'react-native';
3
3
 
4
4
  var MotionPressable = createMotionComponent(Pressable);
@@ -1,4 +1,4 @@
1
- import { createMotionComponent } from './chunk-RIVVBABB.mjs';
1
+ import { createMotionComponent } from './chunk-2ICQLWH2.mjs';
2
2
  import { View } from 'react-native';
3
3
 
4
4
  var MotionView = createMotionComponent(View);
@@ -1,4 +1,4 @@
1
- import { createMotionComponent } from './chunk-RIVVBABB.mjs';
1
+ import { createMotionComponent } from './chunk-2ICQLWH2.mjs';
2
2
  import { Text } from 'react-native';
3
3
 
4
4
  var MotionText = createMotionComponent(Text);
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ var chunk767UKZXG_js = require('./chunk-767UKZXG.js');
4
+ var reactNative = require('react-native');
5
+
6
+ var MotionImage = chunk767UKZXG_js.createMotionComponent(reactNative.Image);
7
+
8
+ exports.MotionImage = MotionImage;
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ var chunk767UKZXG_js = require('./chunk-767UKZXG.js');
4
+ var reactNative = require('react-native');
5
+
6
+ var MotionView = chunk767UKZXG_js.createMotionComponent(reactNative.View);
7
+
8
+ exports.MotionView = MotionView;
@@ -1,4 +1,4 @@
1
- import { createMotionComponent } from './chunk-RIVVBABB.mjs';
1
+ import { createMotionComponent } from './chunk-2ICQLWH2.mjs';
2
2
  import { Image } from 'react-native';
3
3
 
4
4
  var MotionImage = createMotionComponent(Image);
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ var chunk767UKZXG_js = require('./chunk-767UKZXG.js');
4
+ var reactNative = require('react-native');
5
+
6
+ var MotionText = chunk767UKZXG_js.createMotionComponent(reactNative.Text);
7
+
8
+ exports.MotionText = MotionText;
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkVQ5D35UA_js = require('./chunk-VQ5D35UA.js');
3
+ var chunk767UKZXG_js = require('./chunk-767UKZXG.js');
4
4
  var reactNative = require('react-native');
5
5
 
6
- var MotionScrollView = chunkVQ5D35UA_js.createMotionComponent(reactNative.ScrollView);
6
+ var MotionScrollView = chunk767UKZXG_js.createMotionComponent(reactNative.ScrollView);
7
7
 
8
8
  exports.MotionScrollView = MotionScrollView;