narraleaf-react 0.16.1 → 0.17.1

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.
@@ -92,9 +92,13 @@ export declare class LiveGame {
92
92
  *
93
93
  * Every line in between is executed for real, so the backlog and its restore snapshots
94
94
  * accumulate exactly as in normal play — only faster and silent. Audio is muted for the
95
- * duration, in-flight transitions are settled immediately, and timed pauses (`Control.sleep`,
96
- * auto-forward) resolve at once. It stops as soon as a menu is waiting for a choice, so the
97
- * choice itself is always left to the player.
95
+ * duration, and timed pauses (`Control.sleep`, auto-forward) resolve at once. It stops as soon
96
+ * as a menu is waiting for a choice, so the choice itself is always left to the player.
97
+ *
98
+ * Skipping a line is a *request* to the renderer, not a synchronous state change: it is
99
+ * re-issued until the line settles. A line that never responds (an unskippable in-flight
100
+ * media/transition step) ends the run with `"stalled"` rather than hanging — this method always
101
+ * settles.
98
102
  *
99
103
  * Because history accumulates the whole way, {@link getHistory} and
100
104
  * {@link restoreToHistory} cover the fast-forwarded span just like normal play.
@@ -113,9 +117,13 @@ export declare class LiveGame {
113
117
  * unreachable / already-passed id from a successful jump.
114
118
  * @param options.maxSteps - safety bound on the number of advance steps (defaults to the
115
119
  * `maxStackModelLoop` config).
120
+ * @param options.stepTimeout - how long (ms) a single line is given to settle before the run
121
+ * reports `"stalled"`. Defaults to 10000. Raise it if the story
122
+ * fast-forwards through long unskippable media.
116
123
  * @returns why it stopped: `"action"` (reached `until.actionId`), `"menu"`, `"end"` (the stack
117
- * drained), or `"maxSteps"`. When an `actionId` target was requested, `reachedTarget`
118
- * is also set (`true` only for reason `"action"`).
124
+ * drained), `"maxSteps"`, or `"stalled"` (a line refused to settle). When an
125
+ * `actionId` target was requested, `reachedTarget` is also set (`true` only for reason
126
+ * `"action"`).
119
127
  *
120
128
  * Note: only the root execution stack is scanned for the target — an id buried inside an
121
129
  * in-flight parallel (`Control.all`/`any`) or async branch is not a stop point.
@@ -125,8 +133,9 @@ export declare class LiveGame {
125
133
  actionId: string;
126
134
  };
127
135
  maxSteps?: number;
136
+ stepTimeout?: number;
128
137
  }): Promise<{
129
- reason: "menu" | "end" | "maxSteps" | "action";
138
+ reason: "menu" | "end" | "maxSteps" | "action" | "stalled";
130
139
  reachedTarget?: boolean;
131
140
  }>;
132
141
  private assertScreenshot;
@@ -0,0 +1,34 @@
1
+ import type { Scene } from "../../../nlcore/elements/scene";
2
+ import type { Sound } from "../../../nlcore/elements/sound";
3
+ export type ScenePreloadPlan = {
4
+ /**
5
+ * Image urls the scene that is about to paint registers directly: its own backgrounds and
6
+ * images, plus the immediate background of any scene it jumps to. This tier is on the path to
7
+ * the first painted frame, so it is fetched unpaced and nothing is revealed until it is warm.
8
+ */
9
+ critical: string[];
10
+ /**
11
+ * Image urls belonging to the scenes reachable from here, minus anything already in
12
+ * {@link ScenePreloadPlan.critical}. Speculative: warmed after the critical tier, paced, and
13
+ * nothing waits for it.
14
+ */
15
+ lookAhead: string[];
16
+ /** Every url in the plan, in order — the set the cache should keep for this scene. */
17
+ all: string[];
18
+ /**
19
+ * Sounds this scene registers. Warmed alongside the critical tier but never gated on: the
20
+ * audio context can be locked until the player interacts with the page (see
21
+ * `AudioManager.preload`), so waiting for these could wait forever.
22
+ */
23
+ criticalAudio: Sound[];
24
+ };
25
+ /**
26
+ * Split a scene's registered image sources into what must be warm before the scene paints and what
27
+ * is merely likely to be needed soon.
28
+ *
29
+ * The distinction matters because a scene's `srcManager` reaches transitively: `getFutureSrc()`
30
+ * carries the whole asset set of every scene this one can jump to. Treating that as one preload
31
+ * pass meant the first frame of a large story waited on assets from scenes the player had not
32
+ * reached yet.
33
+ */
34
+ export declare function planScenePreload(scene: Scene): ScenePreloadPlan;
@@ -43,6 +43,18 @@ export declare class AudioManager {
43
43
  fromData(data: AudioManagerDataRaw, elementMap: Map<string, LogicAction.GameElement>): this;
44
44
  soundFromData(sound: SoundElement, data: AudioDataRaw): void;
45
45
  isManaged(sound: SoundElement): boolean;
46
+ /**
47
+ * Fetch and decode a sound into the audio cache without playing it, so the first `play()` of
48
+ * this source starts on the same frame it is asked to instead of after a fetch and a decode.
49
+ *
50
+ * Deliberately **not** something to gate a loading screen on: the audio context only becomes
51
+ * ready once the browser's autoplay policy is satisfied by a user gesture, so this can sit
52
+ * pending indefinitely on a page nobody has interacted with yet. Start it and let it land —
53
+ * in practice the gesture that opens a menu unlocks the context long before the scene it
54
+ * belongs to is entered. Failures resolve quietly; the sound then loads on first play, exactly
55
+ * as it did before.
56
+ */
57
+ preload(sound: SoundElement): Promise<void>;
46
58
  reset(): void;
47
59
  setGroupVolume(type: SoundType, volume: number): void;
48
60
  setGlobalVolume(volume: number): void;
@@ -14,19 +14,46 @@ export declare class ImageCacheManager {
14
14
  * the bytes are cached — the first reveal still pays the (async) decode cost and can paint
15
15
  * a blank frame. Decode failures are ignored: the image then simply decodes lazily on
16
16
  * first paint, exactly as before.
17
+ *
18
+ * Returns the element the decode ran on so callers can keep it alive (see
19
+ * {@link ImageCacheManager.preload}'s `retainDecoded`); `null` when the environment has no
20
+ * `Image` or no `decode()`.
17
21
  */
18
22
  private static decodeImage;
19
23
  private src;
20
24
  private preloadTasks;
25
+ /**
26
+ * Decoded images held on purpose. A decoded bitmap only stays in the browser's cache while
27
+ * something still references it, so dropping the element right after `decode()` lets the
28
+ * bitmap be evicted and the reveal decodes all over again. Retention is opt-in per preload
29
+ * (`retainDecoded`) because a full-resolution bitmap costs width × height × 4 bytes — worth
30
+ * it for the scene that is about to paint, far too expensive for a whole reachable graph.
31
+ */
32
+ private decoded;
21
33
  constructor(game: Game);
22
34
  has(name: string): boolean;
23
35
  add(name: string, src: string): this;
24
36
  remove(name: string): this;
25
37
  get(name: string): string | undefined;
38
+ /**
39
+ * Whether this source has been decoded and its decoded bitmap is still held, i.e. attaching
40
+ * it to an `<img>` can paint without an asynchronous decode first.
41
+ */
42
+ isDecoded(name: string): boolean;
26
43
  clear(): this;
27
44
  size(): number;
28
45
  isPreloading(src: string): boolean;
29
- preload(gameState: GameState, url: string): PreloadedToken;
46
+ /**
47
+ * Fetch `url`, cache it as a data URL and decode it, resolving the returned token's
48
+ * `onFinished` only once the decode has settled.
49
+ *
50
+ * @param options.retainDecoded keep the decoded bitmap alive until this source leaves the
51
+ * cache. Use it for the assets that are about to be revealed; leave it off for speculative
52
+ * look-ahead preloading, whose bitmaps would otherwise pile up in memory.
53
+ */
54
+ preload(gameState: GameState, url: string, options?: {
55
+ retainDecoded?: boolean;
56
+ }): PreloadedToken;
30
57
  abortAll(): void;
31
58
  abort(src: string): void;
32
59
  preloadedSrc(): string[];