@symbiote-native/splash-screen 3.0.3 → 3.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 (47) hide show
  1. package/README.md +21 -4
  2. package/build/angular/index.d.ts +1 -1
  3. package/build/angular/services/hide-animation.service/index.js +15 -14
  4. package/build/core/hide-animation-controller.d.ts +3 -1
  5. package/build/core/hide-animation-controller.js +35 -2
  6. package/build/core/index.d.ts +1 -1
  7. package/build/core/native-module.js +18 -0
  8. package/build/core/types.d.ts +6 -0
  9. package/build/react/hooks/use-hide-animation/index.js +14 -18
  10. package/build/react/index.d.ts +1 -1
  11. package/build/solid/index.d.ts +3 -0
  12. package/build/solid/index.js +8 -0
  13. package/build/solid/primitives/create-hide-animation.d.ts +3 -0
  14. package/build/solid/primitives/create-hide-animation.js +24 -0
  15. package/build/svelte/index.d.ts +3 -0
  16. package/build/svelte/index.js +7 -0
  17. package/build/svelte/runes/use-hide-animation.svelte.d.ts +4 -0
  18. package/build/svelte/runes/use-hide-animation.svelte.js +24 -0
  19. package/build/vue/composables/use-hide-animation/index.js +11 -12
  20. package/build/vue/index.d.ts +1 -1
  21. package/build-ngc/angular/index.d.ts +1 -1
  22. package/build-ngc/angular/services/hide-animation.service/index.js +18 -17
  23. package/build-ngc/angular/services/hide-animation.service/index.js.map +1 -1
  24. package/build-ngc/core/hide-animation-controller.d.ts +3 -1
  25. package/build-ngc/core/hide-animation-controller.js +35 -2
  26. package/build-ngc/core/hide-animation-controller.js.map +1 -1
  27. package/build-ngc/core/index.d.ts +1 -1
  28. package/build-ngc/core/native-module.js +18 -0
  29. package/build-ngc/core/native-module.js.map +1 -1
  30. package/build-ngc/core/types.d.ts +6 -0
  31. package/package.json +43 -15
  32. package/src/angular/index.ts +8 -1
  33. package/src/angular/services/hide-animation.service/index.ts +23 -14
  34. package/src/core/hide-animation-controller.ts +45 -3
  35. package/src/core/index.ts +2 -0
  36. package/src/core/native-module.ts +21 -1
  37. package/src/core/types.ts +23 -1
  38. package/src/react/hooks/use-hide-animation/index.ts +16 -21
  39. package/src/react/index.ts +8 -1
  40. package/src/solid/index.ts +17 -0
  41. package/src/solid/primitives/create-hide-animation.ts +37 -0
  42. package/src/svelte/index.ts +16 -0
  43. package/src/svelte/runes/use-hide-animation.svelte.ts +41 -0
  44. package/src/svelte/svelte-ambient.d.ts +6 -0
  45. package/src/vue/composables/use-hide-animation/index.ts +12 -12
  46. package/src/vue/index.ts +8 -1
  47. package/symbiote-splash-screen.podspec +8 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A wrapper package for [SymbioteNative](../../README.md) that makes
4
4
  [`react-native-bootsplash`](https://github.com/zoontek/react-native-bootsplash) usable from
5
- **every** adapter — React, Vue, and Angular — not just React. Unlike a native-*view* wrapper
5
+ **every** adapter — React, Vue, Svelte, Solid, and Angular — not just React. Unlike a native-_view_ wrapper
6
6
  (see `@symbiote-native/slider`), bootsplash exposes an imperative TurboModule (`hide`/`isVisible`)
7
7
  plus a React hook (`useHideAnimation`) that composes a fade-out overlay from plain `View`/`Image`
8
8
  primitives — there is no `ViewConfig` to register, only lifecycle to port per adapter.
@@ -26,6 +26,9 @@ src/core/ hide()/isVisible() (re-exported as-is — zero React upstream),
26
26
  react-native-bootsplash's useHideAnimation body, framework-agnostic
27
27
  src/react/ @symbiote-native/splash-screen/react — useHideAnimation hook
28
28
  src/vue/ @symbiote-native/splash-screen/vue — useHideAnimation composable
29
+ src/svelte/ @symbiote-native/splash-screen/svelte — useHideAnimation rune
30
+ src/solid/ @symbiote-native/splash-screen/solid — createHideAnimation primitive (Solid
31
+ reserves `use*` for consuming existing state)
29
32
  src/angular/ @symbiote-native/splash-screen/angular — HideAnimationService (signals)
30
33
  ```
31
34
 
@@ -109,6 +112,14 @@ import { hide } from '@symbiote-native/splash-screen/angular';
109
112
  // call once from the root component's constructor/ngOnInit.
110
113
  ```
111
114
 
115
+ ```tsx
116
+ // Solid
117
+ import { onMount } from 'solid-js';
118
+ import { hide } from '@symbiote-native/splash-screen/solid';
119
+
120
+ onMount(() => void hide());
121
+ ```
122
+
112
123
  For a fade transition gated on real readiness (layout committed + logo/brand images loaded +
113
124
  your own `ready` flag) instead of an immediate `hide()`, use `useHideAnimation` — it returns the
114
125
  same `{ container, logo, brand }` prop bags upstream's hook does, which you bind onto your own
@@ -128,7 +139,7 @@ const { container, logo } = useHideAnimation({
128
139
  ```
129
140
 
130
141
  See the docs-site package page (`docs/packages/splash-screen`) for the full config surface and
131
- the Vue/Angular equivalents.
142
+ the Vue/Svelte/Solid/Angular equivalents.
132
143
 
133
144
  ## API
134
145
 
@@ -162,14 +173,20 @@ useHideAnimation(config: IHideAnimationConfig): IHideAnimationResult
162
173
  // @symbiote-native/splash-screen/vue — setup runs once, so a getter
163
174
  useHideAnimation(getConfig: () => IHideAnimationConfig): ComputedRef<IHideAnimationResult>
164
175
 
176
+ // @symbiote-native/splash-screen/svelte — the script body runs once, so a getter
177
+ useHideAnimation(getConfig: () => IHideAnimationConfig): { readonly current: IHideAnimationResult }
178
+
179
+ // @symbiote-native/splash-screen/solid — the component body runs once, so an accessor
180
+ createHideAnimation(config: Accessor<IHideAnimationConfig>): Accessor<IHideAnimationResult>
181
+
165
182
  // @symbiote-native/splash-screen/angular — inject(HideAnimationService), connect() once
166
183
  connect(getConfig: () => IHideAnimationConfig): Signal<IHideAnimationResult>
167
184
  ```
168
185
 
169
186
  ## Test it
170
187
 
171
- Headless hook/composable tests live next to each adapter entry
172
- (`src/{react,vue}/{hooks,composables}/use-hide-animation.test.{ts,tsx}`) and mock
188
+ Headless lifecycle-wrapper tests live next to each adapter entry
189
+ (`src/{react,vue,svelte,solid}/{hooks,composables,runes,primitives}/*hide-animation*.test.{ts,tsx}`) and mock
173
190
  `react-native-bootsplash`'s `hide`/`isVisible` plus a fake `__turboModuleProxy` for
174
191
  `getConstants()`, so they run without a real Fabric host. Native asset generation and the
175
192
  Android/iOS wiring above are verified on-device (see the parent [README](../../README.md) for the
@@ -1,3 +1,3 @@
1
1
  export { hide, isVisible } from '../core';
2
2
  export { HideAnimationService } from './services/hide-animation.service';
3
- export type { IHideAnimationConfig, IHideAnimationResult, IManifest, IHideConfig } from '../core';
3
+ export type { IHideAnimationConfig, IHideAnimationFailure, IHideAnimationFailureStage, IHideAnimationResult, IManifest, IHideConfig, } from '../core';
@@ -32,14 +32,12 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { computed, effect, inject, Injectable, Injector } from '@angular/core';
36
- import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../../core/index.js';
35
+ import { computed, effect, inject, Injectable, Injector, } from '@angular/core';
36
+ import { computeHideAnimationStyles, HideAnimationController, } from '../../../core/index.js';
37
37
  // Angular twin of React's `useHideAnimation` hook and Vue's `useHideAnimation` composable.
38
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
39
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
40
- // inside an injection context) with a GETTER, so it can keep reading the caller's own signals —
41
- // the same reason Vue's composable also takes a getter rather than a value: there is no
42
- // re-render/re-setup cycle here to hang a fresh read off of.
38
+ // Angular has no per-instance hook, so `connect()` stands in for it: call it ONCE (typically
39
+ // from a component's field initializer, inside an injection context) with a GETTER so it can
40
+ // keep reading the caller's own signals — same reason Vue's composable takes a getter too.
43
41
  //
44
42
  // readonly hideAnimation = inject(HideAnimationService).connect(() => this.config());
45
43
  // // template: [style]="hideAnimation().container.style", (layout)="..." etc.
@@ -57,16 +55,19 @@ let HideAnimationService = (() => {
57
55
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
58
56
  __runInitializers(_classThis, _classExtraInitializers);
59
57
  }
60
- // Captured in the constructor (itself always run inside an injection context by Angular's
61
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
62
- // code that is not, on its own, an active injection context — mirrors create-tunnel.ts's
63
- // `TunnelOut`, which passes its own captured `Injector` to `effect()` for the same reason.
58
+ // Captured in the constructor (always run inside an injection context by Angular's own DI)
59
+ // so `connect()` can create an `effect()` even when called from field-initializer code that
60
+ // isn't itself an active injection context — mirrors create-tunnel.ts's `TunnelOut`.
64
61
  injector = inject(Injector);
62
+ // The controller reads the native constants in its constructor, so a missing RNBootSplash
63
+ // throws out of connect() and aborts the host component's construction — deliberately: that
64
+ // is a build error, and a splash quietly stuck in light mode would ship unnoticed.
65
65
  connect(getConfig) {
66
66
  const controller = new HideAnimationController(getConfig());
67
- const constants = getHideAnimationConstants();
68
- effect(() => controller.updateConfig(getConfig()), { injector: this.injector });
69
- return computed(() => computeHideAnimationStyles(getConfig(), constants, controller));
67
+ effect(() => controller.updateConfig(getConfig()), {
68
+ injector: this.injector,
69
+ });
70
+ return computed(() => computeHideAnimationStyles(getConfig(), controller.constants, controller));
70
71
  }
71
72
  };
72
73
  return HideAnimationService = _classThis;
@@ -1,6 +1,8 @@
1
- import type { IHideAnimationConfig } from './types';
1
+ import type { IHideAnimationConfig, IHideAnimationConstants } from './types';
2
2
  export declare class HideAnimationController {
3
+ readonly constants: IHideAnimationConstants;
3
4
  private readonly readiness;
5
+ private readonly reportFailure;
4
6
  constructor(config: IHideAnimationConfig);
5
7
  updateConfig(config: IHideAnimationConfig): void;
6
8
  readonly onContainerLayout: () => void;
@@ -1,12 +1,29 @@
1
+ import { dlog } from '@symbiote-native/engine';
1
2
  import { hide } from './hide.js';
3
+ import { getHideAnimationConstants } from './native-module.js';
2
4
  // Faithful port of react-native-bootsplash's useHideAnimation readiness gate (its
3
5
  // src/index.ts): hide() fires exactly once, after layout + both images (if requested) +
4
6
  // the caller all report ready, then the caller's own fade-out `animate()` runs. logoReady/
5
7
  // brandReady are captured ONCE at construction (mirrors the original's useRef factory,
6
8
  // evaluated only on first render) — a config that later drops its logo/brand source does
7
9
  // NOT retroactively flip readiness back on, only updateConfig's animate/userReady do.
10
+ //
11
+ // DELIBERATE DIVERGENCE from upstream, one place only: upstream swallows a rejected hide() with a
12
+ // bare `.catch(() => {})`, which strands the app under a splash it can no longer dismiss (see
13
+ // maybeRunAnimate). We fail open and report instead. Everything else, the unguarded constants read
14
+ // included, stays a faithful port.
8
15
  export class HideAnimationController {
16
+ // Read once here rather than once per adapter: the values never change over a splash's lifetime.
17
+ // Unguarded on purpose — a missing RNBootSplash is a build error that must stay loud, see
18
+ // native-module.ts for the full reasoning before wrapping this in anything.
19
+ constants;
9
20
  readiness;
21
+ // dlog is DEBUG-gated, so it is the developer's seam, not the report: config.onError is the
22
+ // channel the app actually hears about this on.
23
+ reportFailure = (failure) => {
24
+ dlog(`splash-screen: hide animation degraded at "${failure.stage}": ${String(failure.error)}`);
25
+ this.readiness.onError?.(failure);
26
+ };
10
27
  constructor(config) {
11
28
  this.readiness = {
12
29
  layoutReady: false,
@@ -14,11 +31,14 @@ export class HideAnimationController {
14
31
  brandReady: config.manifest.brand == null || config.brand == null,
15
32
  userReady: config.ready ?? true,
16
33
  animate: config.animate,
34
+ onError: config.onError,
17
35
  animateHasBeenCalled: false,
18
36
  };
37
+ this.constants = getHideAnimationConstants();
19
38
  }
20
39
  updateConfig(config) {
21
40
  this.readiness.animate = config.animate;
41
+ this.readiness.onError = config.onError;
22
42
  this.readiness.userReady = config.ready ?? true;
23
43
  this.maybeRunAnimate();
24
44
  }
@@ -42,9 +62,22 @@ export class HideAnimationController {
42
62
  state.userReady &&
43
63
  !state.animateHasBeenCalled) {
44
64
  state.animateHasBeenCalled = true;
65
+ // Fail open. animateHasBeenCalled is already true by the time hide() settles, so the gate
66
+ // is shut for good: on a rejection there is no readiness callback left that could retry,
67
+ // and skipping animate() means the caller's splash overlay never fades out — the app sits
68
+ // under it forever. Running animate() anyway costs at worst a fade over a native splash
69
+ // that is still up; not running it costs the whole app.
45
70
  hide({ fade: false })
46
- .then(() => state.animate())
47
- .catch(() => { });
71
+ .catch((error) => {
72
+ this.reportFailure({ stage: 'hide', error });
73
+ })
74
+ .then(() => {
75
+ state.animate();
76
+ })
77
+ .catch((error) => {
78
+ // The caller's own animation throwing must not become an unhandled rejection either.
79
+ this.reportFailure({ stage: 'animate', error });
80
+ });
48
81
  }
49
82
  }
50
83
  }
@@ -2,4 +2,4 @@ export { hide, isVisible } from './hide';
2
2
  export { HideAnimationController } from './hide-animation-controller';
3
3
  export { computeHideAnimationStyles } from './compute-hide-animation-styles';
4
4
  export { getHideAnimationConstants } from './native-module';
5
- export type { IHideConfig, IManifest, IHideAnimationConfig, IHideAnimationContainerProps, IHideAnimationImageProps, IHideAnimationResult, IHideAnimationConstants, } from './types';
5
+ export type { IHideConfig, IManifest, IHideAnimationConfig, IHideAnimationContainerProps, IHideAnimationFailure, IHideAnimationFailureStage, IHideAnimationImageProps, IHideAnimationResult, IHideAnimationConstants, } from './types';
@@ -7,6 +7,24 @@
7
7
  // (hide/isVisible/useHideAnimation) never exposes directly.
8
8
  import { getEnforcingNativeModule } from '@symbiote-native/engine';
9
9
  const RN_BOOT_SPLASH_MODULE_NAME = 'RNBootSplash';
10
+ // DELIBERATELY UNGUARDED — do not wrap this in a try/catch or hand it a fallback.
11
+ //
12
+ // A missing RNBootSplash is a BUILD error, not a runtime condition: the module is either linked
13
+ // into the binary or it is not, deterministically, on the very first launch. Upstream treats it
14
+ // the same way and does not soften it either — react-native-bootsplash's whole spec file is
15
+ // `TurboModuleRegistry.getEnforcing<Spec>("RNBootSplash")` (src/specs/NativeRNBootSplash.ts),
16
+ // which throws at module-IMPORT time, and its useHideAnimation calls `NativeModule.getConstants()`
17
+ // with no guard at all. `getEnforcing` exists precisely to make this loud.
18
+ //
19
+ // This repo has a live footgun that depends on it staying loud: `npm install` deletes the
20
+ // `.rn-bootsplash/` folder this package's podspec vendors at pod-install time (see CLAUDE.md), so
21
+ // a skipped `pod install` yields exactly this missing module. Degrading to light-mode defaults
22
+ // would turn that into "the splash looks fine, just never dark" and it would ship.
23
+ //
24
+ // Note the failure is currently invisible under @symbiote-native/react specifically: mount()
25
+ // (adapters/react/src/render.ts) wires the reconciler's uncaught-error callbacks to `noop`, so the
26
+ // throw never reaches the app. That is a defect in THAT adapter, tracked separately — it is not a
27
+ // reason to stop throwing here.
10
28
  export function getHideAnimationConstants() {
11
29
  return getEnforcingNativeModule(RN_BOOT_SPLASH_MODULE_NAME).getConstants();
12
30
  }
@@ -16,6 +16,11 @@ export type IManifest = {
16
16
  height: number;
17
17
  };
18
18
  };
19
+ export type IHideAnimationFailureStage = 'hide' | 'animate';
20
+ export type IHideAnimationFailure = {
21
+ stage: IHideAnimationFailureStage;
22
+ error: unknown;
23
+ };
19
24
  export type IHideAnimationConfig = {
20
25
  manifest: IManifest;
21
26
  ready?: boolean;
@@ -24,6 +29,7 @@ export type IHideAnimationConfig = {
24
29
  brand?: IImageSourceProp;
25
30
  darkBrand?: IImageSourceProp;
26
31
  animate: () => void;
32
+ onError?: (failure: IHideAnimationFailure) => void;
27
33
  statusBarTranslucent?: boolean;
28
34
  navigationBarTranslucent?: boolean;
29
35
  };
@@ -1,29 +1,25 @@
1
- // React lifecycle wiring over the framework-agnostic HideAnimationController + style
2
- // computation (core/) — mirrors the lifecycle-bucket naming convention of
3
- // adapters/react/src/hooks. Faithful port of react-native-bootsplash's own useHideAnimation:
4
- // the controller is constructed lazily ONCE (a useRef factory, never reconstructed across
5
- // re-renders), native constants are read once via useState's lazy initializer (they never
6
- // change), and an effect with NO dependency array re-syncs the controller's config after
7
- // EVERY render — intentional, since `ready` flipping true only gets picked up this way.
1
+ // Port of react-native-bootsplash's own useHideAnimation over the framework-agnostic
2
+ // HideAnimationController + style computation in core/. The controller is built lazily ONCE
3
+ // via a useRef factory (never reconstructed across re-renders); the effect below has NO
4
+ // dependency array and re-syncs the controller's config after EVERY render on purpose, since
5
+ // `ready` flipping true is only picked up that way. The native constants ride along on that same
6
+ // once-built controller, so there is no separate useState to hold them.
8
7
  //
9
- // No useMemo around the style computation: upstream lists ~15 individual primitive fields
10
- // (manifest.logo.width, backgroundColor, ...) as its memo's deps specifically so Object.is
11
- // compares by VALUE, not by the `config`/`manifest` object's reference — callers construct
12
- // that object fresh on every render (the normal way to call this hook), so keying a memo off
13
- // the whole object would recompute every render anyway while looking like it memoizes.
14
- // Reproducing the fine-grained list isn't worth it for a splash screen shown for a couple of
15
- // renders at boot; computing plainly is honest about the actual cost and simpler to read.
16
- import { useEffect, useRef, useState } from 'react';
17
- import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../../core/index.js';
8
+ // No useMemo around the style computation: upstream memoizes on ~15 individual primitive
9
+ // fields so Object.is compares by value rather than by the config/manifest object's
10
+ // reference (callers construct that object fresh every render, so keying off the whole
11
+ // object would recompute anyway). Not worth reproducing for a splash screen shown a couple
12
+ // of renders at boot — computing plainly is simpler and equally cheap.
13
+ import { useEffect, useRef } from 'react';
14
+ import { computeHideAnimationStyles, HideAnimationController, } from '../../../core/index.js';
18
15
  export function useHideAnimation(config) {
19
16
  const controllerRef = useRef(null);
20
17
  if (controllerRef.current == null) {
21
18
  controllerRef.current = new HideAnimationController(config);
22
19
  }
23
20
  const controller = controllerRef.current;
24
- const [constants] = useState(() => getHideAnimationConstants());
25
21
  useEffect(() => {
26
22
  controller.updateConfig(config);
27
23
  });
28
- return computeHideAnimationStyles(config, constants, controller);
24
+ return computeHideAnimationStyles(config, controller.constants, controller);
29
25
  }
@@ -1,3 +1,3 @@
1
1
  export { hide, isVisible } from '../core';
2
2
  export { useHideAnimation } from './hooks/use-hide-animation';
3
- export type { IHideAnimationConfig, IHideAnimationResult, IManifest, IHideConfig } from '../core';
3
+ export type { IHideAnimationConfig, IHideAnimationFailure, IHideAnimationFailureStage, IHideAnimationResult, IManifest, IHideConfig, } from '../core';
@@ -0,0 +1,3 @@
1
+ export { hide, isVisible } from '../core';
2
+ export { createHideAnimation } from './primitives/create-hide-animation';
3
+ export type { IHideAnimationConfig, IHideAnimationFailure, IHideAnimationFailureStage, IHideAnimationResult, IManifest, IHideConfig, } from '../core';
@@ -0,0 +1,8 @@
1
+ // @symbiote-native/splash-screen/solid: the Solid entry over the framework-agnostic core.
2
+ // hide/isVisible carry zero lifecycle and are re-exported verbatim; createHideAnimation wraps
3
+ // HideAnimationController + computeHideAnimationStyles with Solid's own reactivity
4
+ // (primitives/create-hide-animation.ts) — mirrors the lifecycle-bucket naming convention of
5
+ // adapters/solid/src/primitives (never `hooks`/`composables`/`runes`, those are React's, Vue's
6
+ // and Svelte's terms).
7
+ export { hide, isVisible } from '../core/index.js';
8
+ export { createHideAnimation } from './primitives/create-hide-animation.js';
@@ -0,0 +1,3 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import { type IHideAnimationConfig, type IHideAnimationResult } from '../../core';
3
+ export declare function createHideAnimation(config: Accessor<IHideAnimationConfig>): Accessor<IHideAnimationResult>;
@@ -0,0 +1,24 @@
1
+ // Solid lifecycle wiring over the framework-agnostic HideAnimationController + style computation
2
+ // (core/) — the Solid twin of React's useHideAnimation hook, Vue's composable and Svelte's rune.
3
+ //
4
+ // `primitives/` and `create*`, never `hooks/`+`use*`: Solid's ecosystem calls a composable
5
+ // reactive function a PRIMITIVE and reserves `use*` for consuming something that already exists.
6
+ // Full rationale in adapters/solid/src/primitives/create-color-scheme.ts.
7
+ //
8
+ // Takes a config ACCESSOR, not a plain value, and returns one: a Solid component body runs ONCE
9
+ // (like Vue's setup, unlike a React hook re-running every render), so a plain config would freeze
10
+ // at its boot value and `ready` flipping true would never be seen. The effect below re-syncs the
11
+ // controller on every signal the accessor reads, mirroring Vue's watchEffect.
12
+ //
13
+ // The controller stays a plain local, never a signal: only its methods are called. It also carries
14
+ // the native constants, read once in its constructor — so a missing RNBootSplash throws straight
15
+ // out of the primitive body, on purpose.
16
+ import { createEffect, createMemo } from 'solid-js';
17
+ import { computeHideAnimationStyles, HideAnimationController, } from '../../core/index.js';
18
+ export function createHideAnimation(config) {
19
+ const controller = new HideAnimationController(config());
20
+ createEffect(() => {
21
+ controller.updateConfig(config());
22
+ });
23
+ return createMemo(() => computeHideAnimationStyles(config(), controller.constants, controller));
24
+ }
@@ -0,0 +1,3 @@
1
+ export { hide, isVisible } from '../core';
2
+ export { useHideAnimation } from './runes/use-hide-animation.svelte';
3
+ export type { IHideAnimationConfig, IHideAnimationFailure, IHideAnimationFailureStage, IHideAnimationResult, IManifest, IHideConfig, } from '../core';
@@ -0,0 +1,7 @@
1
+ // @symbiote-native/splash-screen/svelte: the Svelte entry over the framework-agnostic core.
2
+ // hide/isVisible carry zero lifecycle and are re-exported verbatim; useHideAnimation wraps
3
+ // HideAnimationController + computeHideAnimationStyles with Svelte's own runes
4
+ // (runes/use-hide-animation.svelte.ts) — mirrors the lifecycle-bucket naming convention of
5
+ // adapters/svelte/src/runes (never `hooks`/`composables`, those are React's/Vue's terms).
6
+ export { hide, isVisible } from '../core/index.js';
7
+ export { useHideAnimation } from './runes/use-hide-animation.svelte';
@@ -0,0 +1,4 @@
1
+ import { type IHideAnimationConfig, type IHideAnimationResult } from '../../core';
2
+ export declare function useHideAnimation(getConfig: () => IHideAnimationConfig): {
3
+ readonly current: IHideAnimationResult;
4
+ };
@@ -0,0 +1,24 @@
1
+ // Svelte lifecycle wiring over the framework-agnostic HideAnimationController + style
2
+ // computation (core/). A Svelte component's `<script>` body runs ONCE (like Vue's setup,
3
+ // unlike a React hook re-running every render), so this takes a config GETTER, not a plain
4
+ // value — the boxed-getter convention from use-color-scheme.svelte.ts. The controller stays
5
+ // a plain local, not `$state`: only its methods are called, nothing needs Svelte to react to
6
+ // the reference itself. `$effect` re-syncs the controller's config on every reactive read
7
+ // inside `getConfig()`, mirroring Vue's `watchEffect`. The controller also carries the native
8
+ // constants, read once in its constructor — so a missing RNBootSplash throws straight out of the
9
+ // component's script body, on purpose.
10
+ import { computeHideAnimationStyles, HideAnimationController, } from '../../core/index.js';
11
+ export function useHideAnimation(getConfig) {
12
+ const controller = new HideAnimationController(getConfig());
13
+ let result = $state(computeHideAnimationStyles(getConfig(), controller.constants, controller));
14
+ $effect(() => {
15
+ const config = getConfig();
16
+ controller.updateConfig(config);
17
+ result = computeHideAnimationStyles(config, controller.constants, controller);
18
+ });
19
+ return {
20
+ get current() {
21
+ return result;
22
+ },
23
+ };
24
+ }
@@ -1,21 +1,20 @@
1
1
  // Vue lifecycle wiring over the framework-agnostic HideAnimationController + style
2
- // computation (core/). A Vue composable's setup body runs ONCE (unlike a React hook,
3
- // which re-runs every render), so this takes a config GETTER, not a plain value — Vue's
4
- // reactivity tracks whatever reactive refs the getter reads internally and re-runs the
5
- // watchEffect/computed below on their change, mirroring how a React consumer would pass a
6
- // fresh config object on every render.
2
+ // computation (core/). A composable's setup body runs ONCE (unlike a React hook, which
3
+ // re-runs every render), so this takes a config GETTER, not a plain value — Vue's
4
+ // reactivity tracks whatever reactive refs the getter reads and re-runs the
5
+ // watchEffect/computed below on their change, mirroring a React consumer passing a fresh
6
+ // config object every render.
7
7
  //
8
- // The controller is a plain local, not a Vue ref: nothing needs Vue to react to the
9
- // controller reference itself (only its methods are called), so wrapping it would only
10
- // add an unnecessary reactive Proxy — same identity discipline as
11
- // `use-color-scheme.ts`'s subscription handle.
8
+ // The controller is a plain local, not a Vue ref: only its methods are called, so wrapping
9
+ // it would only add an unnecessary reactive Proxy — same identity discipline as
10
+ // `use-color-scheme.ts`'s subscription handle. It also carries the native constants, read once in
11
+ // its constructor — so a missing RNBootSplash throws straight out of setup, on purpose.
12
12
  import { computed, watchEffect } from '@vue/runtime-core';
13
- import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../../core/index.js';
13
+ import { computeHideAnimationStyles, HideAnimationController, } from '../../../core/index.js';
14
14
  export function useHideAnimation(getConfig) {
15
15
  const controller = new HideAnimationController(getConfig());
16
- const constants = getHideAnimationConstants();
17
16
  watchEffect(() => {
18
17
  controller.updateConfig(getConfig());
19
18
  });
20
- return computed(() => computeHideAnimationStyles(getConfig(), constants, controller));
19
+ return computed(() => computeHideAnimationStyles(getConfig(), controller.constants, controller));
21
20
  }
@@ -1,3 +1,3 @@
1
1
  export { hide, isVisible } from '../core';
2
2
  export { useHideAnimation } from './composables/use-hide-animation';
3
- export type { IHideAnimationConfig, IHideAnimationResult, IManifest, IHideConfig } from '../core';
3
+ export type { IHideAnimationConfig, IHideAnimationFailure, IHideAnimationFailureStage, IHideAnimationResult, IManifest, IHideConfig, } from '../core';
@@ -1,3 +1,3 @@
1
1
  export { hide, isVisible } from '../core';
2
2
  export { HideAnimationService } from './services/hide-animation.service';
3
- export type { IHideAnimationConfig, IHideAnimationResult, IManifest, IHideConfig } from '../core';
3
+ export type { IHideAnimationConfig, IHideAnimationFailure, IHideAnimationFailureStage, IHideAnimationResult, IManifest, IHideConfig, } from '../core';
@@ -1,31 +1,32 @@
1
- import { computed, effect, inject, Injectable, Injector } from '@angular/core';
2
- import { computeHideAnimationStyles, getHideAnimationConstants, HideAnimationController, } from '../../../core';
1
+ import { computed, effect, inject, Injectable, Injector, } from '@angular/core';
2
+ import { computeHideAnimationStyles, HideAnimationController, } from '../../../core';
3
3
  import * as i0 from "@angular/core";
4
4
  // Angular twin of React's `useHideAnimation` hook and Vue's `useHideAnimation` composable.
5
- // Angular has no per-instance hook — state and lifecycle live in DI instead, so `connect()`
6
- // stands in for the hook's role: call it ONCE (typically from a component's field initializer,
7
- // inside an injection context) with a GETTER, so it can keep reading the caller's own signals —
8
- // the same reason Vue's composable also takes a getter rather than a value: there is no
9
- // re-render/re-setup cycle here to hang a fresh read off of.
5
+ // Angular has no per-instance hook, so `connect()` stands in for it: call it ONCE (typically
6
+ // from a component's field initializer, inside an injection context) with a GETTER so it can
7
+ // keep reading the caller's own signals — same reason Vue's composable takes a getter too.
10
8
  //
11
9
  // readonly hideAnimation = inject(HideAnimationService).connect(() => this.config());
12
10
  // // template: [style]="hideAnimation().container.style", (layout)="..." etc.
13
11
  export class HideAnimationService {
14
- // Captured in the constructor (itself always run inside an injection context by Angular's
15
- // own DI) so `connect()` can create an `effect()` even when called from plain field-initializer
16
- // code that is not, on its own, an active injection context — mirrors create-tunnel.ts's
17
- // `TunnelOut`, which passes its own captured `Injector` to `effect()` for the same reason.
12
+ // Captured in the constructor (always run inside an injection context by Angular's own DI)
13
+ // so `connect()` can create an `effect()` even when called from field-initializer code that
14
+ // isn't itself an active injection context — mirrors create-tunnel.ts's `TunnelOut`.
18
15
  injector = inject(Injector);
16
+ // The controller reads the native constants in its constructor, so a missing RNBootSplash
17
+ // throws out of connect() and aborts the host component's construction — deliberately: that
18
+ // is a build error, and a splash quietly stuck in light mode would ship unnoticed.
19
19
  connect(getConfig) {
20
20
  const controller = new HideAnimationController(getConfig());
21
- const constants = getHideAnimationConstants();
22
- effect(() => controller.updateConfig(getConfig()), { injector: this.injector });
23
- return computed(() => computeHideAnimationStyles(getConfig(), constants, controller));
21
+ effect(() => controller.updateConfig(getConfig()), {
22
+ injector: this.injector,
23
+ });
24
+ return computed(() => computeHideAnimationStyles(getConfig(), controller.constants, controller));
24
25
  }
25
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: HideAnimationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
26
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: HideAnimationService, providedIn: 'root' });
26
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HideAnimationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
27
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HideAnimationService, providedIn: 'root' });
27
28
  }
28
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: HideAnimationService, decorators: [{
29
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HideAnimationService, decorators: [{
29
30
  type: Injectable,
30
31
  args: [{ providedIn: 'root' }]
31
32
  }] });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/hide-animation.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAe,MAAM,eAAe,CAAC;AAC5F,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,GAGxB,MAAM,eAAe,CAAC;;AAEvB,2FAA2F;AAC3F,4FAA4F;AAC5F,+FAA+F;AAC/F,gGAAgG;AAChG,wFAAwF;AACxF,6DAA6D;AAC7D,EAAE;AACF,wFAAwF;AACxF,gFAAgF;AAEhF,MAAM,OAAO,oBAAoB;IAC/B,0FAA0F;IAC1F,gGAAgG;IAChG,yFAAyF;IACzF,2FAA2F;IAC1E,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO,CAAC,SAAqC;QAC3C,MAAM,UAAU,GAAG,IAAI,uBAAuB,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,yBAAyB,EAAE,CAAC;QAE9C,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEhF,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IACxF,CAAC;uGAdU,oBAAoB;2GAApB,oBAAoB,cADP,MAAM;;2FACnB,oBAAoB;kBADhC,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/hide-animation.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,MAAM,EACN,MAAM,EACN,UAAU,EACV,QAAQ,GAET,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GAGxB,MAAM,eAAe,CAAC;;AAEvB,2FAA2F;AAC3F,6FAA6F;AAC7F,6FAA6F;AAC7F,2FAA2F;AAC3F,EAAE;AACF,wFAAwF;AACxF,gFAAgF;AAEhF,MAAM,OAAO,oBAAoB;IAC/B,2FAA2F;IAC3F,4FAA4F;IAC5F,qFAAqF;IACpE,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,0FAA0F;IAC1F,4FAA4F;IAC5F,mFAAmF;IACnF,OAAO,CAAC,SAAqC;QAC3C,MAAM,UAAU,GAAG,IAAI,uBAAuB,CAAC,SAAS,EAAE,CAAC,CAAC;QAE5D,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE;YACjD,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,GAAG,EAAE,CACnB,0BAA0B,CAAC,SAAS,EAAE,EAAE,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,CAC1E,CAAC;IACJ,CAAC;uGAnBU,oBAAoB;2GAApB,oBAAoB,cADP,MAAM;;2FACnB,oBAAoB;kBADhC,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
@@ -1,6 +1,8 @@
1
- import type { IHideAnimationConfig } from './types';
1
+ import type { IHideAnimationConfig, IHideAnimationConstants } from './types';
2
2
  export declare class HideAnimationController {
3
+ readonly constants: IHideAnimationConstants;
3
4
  private readonly readiness;
5
+ private readonly reportFailure;
4
6
  constructor(config: IHideAnimationConfig);
5
7
  updateConfig(config: IHideAnimationConfig): void;
6
8
  readonly onContainerLayout: () => void;
@@ -1,12 +1,29 @@
1
+ import { dlog } from '@symbiote-native/engine';
1
2
  import { hide } from './hide';
3
+ import { getHideAnimationConstants } from './native-module';
2
4
  // Faithful port of react-native-bootsplash's useHideAnimation readiness gate (its
3
5
  // src/index.ts): hide() fires exactly once, after layout + both images (if requested) +
4
6
  // the caller all report ready, then the caller's own fade-out `animate()` runs. logoReady/
5
7
  // brandReady are captured ONCE at construction (mirrors the original's useRef factory,
6
8
  // evaluated only on first render) — a config that later drops its logo/brand source does
7
9
  // NOT retroactively flip readiness back on, only updateConfig's animate/userReady do.
10
+ //
11
+ // DELIBERATE DIVERGENCE from upstream, one place only: upstream swallows a rejected hide() with a
12
+ // bare `.catch(() => {})`, which strands the app under a splash it can no longer dismiss (see
13
+ // maybeRunAnimate). We fail open and report instead. Everything else, the unguarded constants read
14
+ // included, stays a faithful port.
8
15
  export class HideAnimationController {
16
+ // Read once here rather than once per adapter: the values never change over a splash's lifetime.
17
+ // Unguarded on purpose — a missing RNBootSplash is a build error that must stay loud, see
18
+ // native-module.ts for the full reasoning before wrapping this in anything.
19
+ constants;
9
20
  readiness;
21
+ // dlog is DEBUG-gated, so it is the developer's seam, not the report: config.onError is the
22
+ // channel the app actually hears about this on.
23
+ reportFailure = (failure) => {
24
+ dlog(`splash-screen: hide animation degraded at "${failure.stage}": ${String(failure.error)}`);
25
+ this.readiness.onError?.(failure);
26
+ };
10
27
  constructor(config) {
11
28
  this.readiness = {
12
29
  layoutReady: false,
@@ -14,11 +31,14 @@ export class HideAnimationController {
14
31
  brandReady: config.manifest.brand == null || config.brand == null,
15
32
  userReady: config.ready ?? true,
16
33
  animate: config.animate,
34
+ onError: config.onError,
17
35
  animateHasBeenCalled: false,
18
36
  };
37
+ this.constants = getHideAnimationConstants();
19
38
  }
20
39
  updateConfig(config) {
21
40
  this.readiness.animate = config.animate;
41
+ this.readiness.onError = config.onError;
22
42
  this.readiness.userReady = config.ready ?? true;
23
43
  this.maybeRunAnimate();
24
44
  }
@@ -42,9 +62,22 @@ export class HideAnimationController {
42
62
  state.userReady &&
43
63
  !state.animateHasBeenCalled) {
44
64
  state.animateHasBeenCalled = true;
65
+ // Fail open. animateHasBeenCalled is already true by the time hide() settles, so the gate
66
+ // is shut for good: on a rejection there is no readiness callback left that could retry,
67
+ // and skipping animate() means the caller's splash overlay never fades out — the app sits
68
+ // under it forever. Running animate() anyway costs at worst a fade over a native splash
69
+ // that is still up; not running it costs the whole app.
45
70
  hide({ fade: false })
46
- .then(() => state.animate())
47
- .catch(() => { });
71
+ .catch((error) => {
72
+ this.reportFailure({ stage: 'hide', error });
73
+ })
74
+ .then(() => {
75
+ state.animate();
76
+ })
77
+ .catch((error) => {
78
+ // The caller's own animation throwing must not become an unhandled rejection either.
79
+ this.reportFailure({ stage: 'animate', error });
80
+ });
48
81
  }
49
82
  }
50
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hide-animation-controller.js","sourceRoot":"","sources":["../../src/core/hide-animation-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAY9B,kFAAkF;AAClF,wFAAwF;AACxF,2FAA2F;AAC3F,uFAAuF;AACvF,yFAAyF;AACzF,sFAAsF;AACtF,MAAM,OAAO,uBAAuB;IACjB,SAAS,CAAkB;IAE5C,YAAY,MAA4B;QACtC,IAAI,CAAC,SAAS,GAAG;YACf,WAAW,EAAE,KAAK;YAClB,SAAS,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI;YAC9B,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI;YACjE,SAAS,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI;YAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,oBAAoB,EAAE,KAAK;SAC5B,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,MAA4B;QACvC,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QACxC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC;QAChD,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAEQ,iBAAiB,GAAG,GAAS,EAAE;QACtC,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;QAClC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC;IAEO,aAAa,GAAG,GAAS,EAAE;QAClC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC;IAEO,cAAc,GAAG,GAAS,EAAE;QACnC,IAAI,CAAC,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC;IAEM,eAAe;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAE7B,IACE,KAAK,CAAC,WAAW;YACjB,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,SAAS;YACf,CAAC,KAAK,CAAC,oBAAoB,EAC3B,CAAC;YACD,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAClC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;iBAClB,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;iBAC3B,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"hide-animation-controller.js","sourceRoot":"","sources":["../../src/core/hide-animation-controller.ts"],"names":[],"mappings":"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"}