narraleaf-react 0.47.0 → 0.47.2

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.
package/dist/built-in.js CHANGED
@@ -1 +1 @@
1
- import{Gallery as L,DevTools as S}from"narraleaf-react";import{Control as r,Image as b,Layer as T,Transform as h}from"narraleaf-react";var d=new WeakMap;function y(e,t={}){let n=d.get(e);if(n)return n;let o=new T(t.name??"[[Effect Layer of "+e.config.name+"]]",{zIndex:t.zIndex??1e3});return e.config.layers.push(o),d.set(e,o),o}function k(e,t={}){let{closeDuration:n=180,hold:o=100,openDuration:p=220,easing:i="easeInOut",color:l="#000"}=t,f=y(e),a=u("[[Blink Top of "+e.config.name+"]]",l,f),s=u("[[Blink Bottom of "+e.config.name+"]]",l,f);return r.do([r.all([a.transform(c([[{opacity:1,clipPath:"inset(0 0 100% 0)"},{duration:0}],[{clipPath:"inset(0 0 50% 0)"},{duration:n,ease:i}]])),s.transform(c([[{opacity:1,clipPath:"inset(100% 0 0 0)"},{duration:0}],[{clipPath:"inset(50% 0 0 0)"},{duration:n,ease:i}]]))]),...o>0?[r.sleep(o)]:[],r.all([a.transform(c([[{clipPath:"inset(0 0 50% 0)"},{duration:0}],[{clipPath:"inset(0 0 100% 0)"},{duration:p,ease:i}],[{opacity:0,clipPath:"inset(0 0 100% 0)"},{duration:0}]])),s.transform(c([[{clipPath:"inset(50% 0 0 0)"},{duration:0}],[{clipPath:"inset(100% 0 0 0)"},{duration:p,ease:i}],[{opacity:0,clipPath:"inset(100% 0 0 0)"},{duration:0}]]))]),r.all([r.do([a.opacity(0,0),a.clearClip({duration:0})]),r.do([s.opacity(0,0),s.clearClip({duration:0})])])])}function E(e,t={}){let{duration:n=300,opacity:o=.72,color:p="#000",inner:i="44%",outer:l="78%",hold:f=600,easing:a="easeInOut"}=t,s=y(e),m=u("[[Vignette of "+e.config.name+"]]",p,s),g=`radial-gradient(circle at center, transparent ${i}, black ${l})`;return r.do([m.transform(c([[{opacity:0,maskImage:g,maskSize:"100% 100%",maskPosition:"center",maskRepeat:"no-repeat",maskMode:"alpha"},{duration:0}],[{opacity:o},{duration:n,ease:a}]])),...f>0?[r.sleep(f)]:[],m.transform(c([[{opacity:0},{duration:n,ease:a}]])),m.clearMask({duration:0})])}function u(e,t,n){return new b({name:e,src:t,autoFit:!0,opacity:0,layer:n})}function c(e){return new h(e.map(([t,n])=>({props:t,options:n})))}export{S as DevTools,L as Gallery,k as blink,y as effectLayer,E as vignette};
1
+ import{Gallery as L,DevTools as S}from"narraleaf-react";import{Control as r,Image as b,Layer as T,Transform as h}from"narraleaf-react";var d=new WeakMap;function y(e,t={}){let n=d.get(e);if(n)return n;let o=new T(t.name??"[[Effect Layer of "+e.config.name+"]]",{zIndex:t.zIndex??1e3});return e.config.layers.push(o),d.set(e,o),o}function k(e,t={}){let{closeDuration:n=180,hold:o=100,openDuration:p=220,easing:i="easeInOut",color:l="#000"}=t,f=y(e),a=u("[[Blink Top of "+e.config.name+"]]",l,f),s=u("[[Blink Bottom of "+e.config.name+"]]",l,f);return r.do([r.all([a.transform(c([[{opacity:1,clipPath:"inset(0 0 100% 0)"},{duration:0}],[{clipPath:"inset(0 0 50% 0)"},{duration:n,ease:i}]])),s.transform(c([[{opacity:1,clipPath:"inset(100% 0 0 0)"},{duration:0}],[{clipPath:"inset(50% 0 0 0)"},{duration:n,ease:i}]]))]),...o>0?[r.sleep(o)]:[],r.all([a.transform(c([[{clipPath:"inset(0 0 50% 0)"},{duration:0}],[{clipPath:"inset(0 0 100% 0)"},{duration:p,ease:i}],[{opacity:0,clipPath:"inset(0 0 100% 0)"},{duration:0}]])),s.transform(c([[{clipPath:"inset(50% 0 0 0)"},{duration:0}],[{clipPath:"inset(100% 0 0 0)"},{duration:p,ease:i}],[{opacity:0,clipPath:"inset(100% 0 0 0)"},{duration:0}]]))]),r.all([r.do([a.opacity(0,0),a.clearClip({duration:0})]),r.do([s.opacity(0,0),s.clearClip({duration:0})])])])}function E(e,t={}){let{duration:n=300,opacity:o=.72,color:p="#000",inner:i="44%",outer:l="78%",hold:f=600,easing:a="easeInOut"}=t,s=y(e),m=u("[[Vignette of "+e.config.name+"]]",p,s),g=`radial-gradient(circle at center, transparent ${i}, black ${l})`;return r.do([m.transform(c([[{opacity:0,maskImage:g,maskSize:"100% 100%",maskPosition:"50% 50%",maskRepeat:"no-repeat",maskMode:"alpha"},{duration:0}],[{opacity:o},{duration:n,ease:a}]])),...f>0?[r.sleep(f)]:[],m.transform(c([[{opacity:0},{duration:n,ease:a}]])),m.clearMask({duration:0})])}function u(e,t,n){return new b({name:e,src:t,autoFit:!0,opacity:0,layer:n})}function c(e){return new h(e.map(([t,n])=>({props:t,options:n})))}export{S as DevTools,L as Gallery,k as blink,y as effectLayer,E as vignette};
@@ -46,12 +46,20 @@ export declare class SceneAction<T extends typeof SceneActionTypes[keyof typeof
46
46
  * Waits until the previous BGM has completely faded out (if any) before
47
47
  * resolving, ensuring seamless audio transition when jumping between scenes.
48
48
  *
49
- * A **suspended** scene starts nothing. This is the one place every path that starts a scene's
49
+ * A **suspended** scene starts nothing, and neither does a scene whose music a load has
50
+ * already put back and left playing. This is the one place every path that starts a scene's
50
51
  * music passes through, and it has to be the guard, because the call that reaches it is not
51
52
  * always the one that asked: `getExposedStateAsync` waits on a component that is not mounted
52
53
  * yet, and a stage remount - which is what loading a save performs - fires those waiting
53
54
  * callbacks all over again. A caller parked behind a scene call therefore came back from a save
54
55
  * with its music playing over the scene it had called.
56
+ *
57
+ * The same reason makes the second refusal necessary rather than something the load could sort
58
+ * out for itself. Every host applies a save as `newGame().deserialize(saved)`, and `newGame()`
59
+ * mounts the entry scene *first*: the callback that starts its music is armed before the load
60
+ * has run at all, and comes back once the load has finished restoring the audio. Whatever the
61
+ * load arms or declines to arm, that one is already in flight - so the refusal has to be here,
62
+ * where the order the callbacks were armed in cannot matter.
55
63
  */
56
64
  static initBackgroundMusic(scene: Scene, exposed: ExposedState[ExposedStateType.scene], state?: GameState): Promise<void>;
57
65
  static createSceneSnapshot(scene: Scene, state: GameState): SceneSnapshot;
@@ -93,6 +93,20 @@ export declare class Timeline {
93
93
  setGuard(guard: GameStateGuard): this;
94
94
  protected catSettled(): [resolved: Timeline[], cancelled: Timeline[]];
95
95
  protected onTimelineRegistered(callback: () => void): this;
96
+ /**
97
+ * Work out the status again, after the awaitable or one of the children has settled.
98
+ *
99
+ * A settled timeline keeps the status it settled with. Children still settle after it -
100
+ * aborting a timeline aborts its children, and each of them reports back here - and that is
101
+ * the abort running its course, not a second outcome.
102
+ *
103
+ * A resolved awaitable is not an aborted one, although its skip controller says it is:
104
+ * `Awaitable.resolve` retires the controller with `cancel()`, which leaves `isAborted()` true.
105
+ * Reading the controller alone called a timeline cancelled the moment its awaitable resolved
106
+ * while a child was still running - a transform or transition action, whose own awaitable
107
+ * resolves one step before the animation it waits for, or a line whose voice outlives it - and
108
+ * then refused the `resolved` the child brought when it finished.
109
+ */
96
110
  private resolveStatus;
97
111
  private emitEvents;
98
112
  /**
@@ -79,6 +79,20 @@ export declare class AudioManager {
79
79
  * whether the reference was really taken, which a release has to wait for.
80
80
  */
81
81
  private retained;
82
+ /**
83
+ * What the most recent load put on the wire, keyed by the clip it put there.
84
+ *
85
+ * Written the moment {@link AudioManager.fromData} is handed a record - before any of the
86
+ * fetching and decoding it starts - because the question it answers is asked by callers that
87
+ * run a turn or two later and must not depend on having lost a race with the restore.
88
+ *
89
+ * An entry stands until something else takes the clip over: every other transport that starts
90
+ * or ends a clip drops it, so the answer can only be "yes" while the load really is the reason
91
+ * that clip is where it is. The value is whether the record wanted the clip *running* - a
92
+ * stopped or paused one is restored just as faithfully, but it is not something a scene start
93
+ * would be taking away.
94
+ */
95
+ private restoredByLoad;
82
96
  private globalVolume;
83
97
  private sound;
84
98
  private ready;
@@ -252,6 +266,25 @@ export declare class AudioManager {
252
266
  fromData(data: AudioManagerDataRaw, elementMap: Map<string, LogicAction.GameElement>): this;
253
267
  soundFromData(sound: SoundElement, data: AudioDataRaw): void;
254
268
  isManaged(sound: SoundElement): boolean;
269
+ /**
270
+ * Whether the most recent load is the reason `sound` is on the wire, and left it running.
271
+ *
272
+ * The question a scene has to ask before it starts its own background music: the clip the save
273
+ * restored *is* that music already, playing at the position the player saved, and starting it
274
+ * again is a cross-fade of the track into itself - which stops it and plays it from the top.
275
+ * That throws the saved position away, and the stop lands while the restored element's `play()`
276
+ * is still pending, which the browser reports as an `AbortError`.
277
+ *
278
+ * Answered from the record rather than from the live token on purpose. A restore takes several
279
+ * turns to fetch and start a clip, and a scene mounting can get here first; reading
280
+ * `isPlaying()` would then say "nothing is playing" and the scene would start the very clip the
281
+ * load is in the middle of putting back. The record is written synchronously, so this is true
282
+ * from the instant the load is asked for.
283
+ *
284
+ * A record that says the clip is stopped or paused is not "running": the save wants silence
285
+ * there, and a scene that would have started music still does.
286
+ */
287
+ isRunningFromLoad(sound: SoundElement): boolean;
255
288
  /**
256
289
  * Fetch and decode a sound into the audio cache without playing it, so the first `play()` of
257
290
  * this source starts on the same frame it is asked to instead of after a fetch and a decode.