narraleaf-react 0.16.0 → 0.16.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.
@@ -5,14 +5,16 @@ import { ImageTransition } from "../elements/transition/transitions/image/imageT
5
5
  import { TextTransition } from "../elements/transition/transitions/text/textTransition";
6
6
  import { BlurDissolve } from "../elements/transition/transitions/image/blurDissolve";
7
7
  import { Push } from "../elements/transition/transitions/image/push";
8
+ import { Darkness } from "../elements/transition/transitions/image/darkness";
8
9
  import { ThroughColor } from "../elements/transition/transitions/image/throughColor";
9
10
  import { Reveal } from "../elements/transition/transitions/image/reveal";
10
11
  import { Mask } from "../elements/transition/transitions/image/mask";
11
- export { Transition, ImageTransition, TextTransition, Dissolve, FadeIn, BlurDissolve, Push, ThroughColor, Reveal, Mask, };
12
+ export { Transition, ImageTransition, TextTransition, Dissolve, FadeIn, BlurDissolve, Push, Darkness, ThroughColor, Reveal, Mask, };
12
13
  export type { DissolveOptions } from "../elements/transition/transitions/image/dissolve";
13
14
  export type { FadeInOptions } from "../elements/transition/transitions/image/fadeIn";
14
15
  export type { BlurDissolveOptions } from "../elements/transition/transitions/image/blurDissolve";
15
16
  export type { PushOptions } from "../elements/transition/transitions/image/push";
17
+ export type { DarknessOptions } from "../elements/transition/transitions/image/darkness";
16
18
  export type { ThroughColorOptions, ThroughColorUncover, } from "../elements/transition/transitions/image/throughColor";
17
19
  export type { RevealOptions } from "../elements/transition/transitions/image/reveal";
18
20
  export type { MaskPattern, WipePatternOptions, BarnDoorPatternOptions, IrisPatternOptions, ClockPatternOptions, FanPatternOptions, BlindsPatternOptions, DotsPatternOptions, } from "../elements/transition/transitions/image/mask";
@@ -34,6 +34,14 @@ export type TextEventConfig = {
34
34
  *
35
35
  * First version carries a closed set: a character-expression switch and/or a sound effect.
36
36
  *
37
+ * **Skip / instant reveal semantics.** Skipping the typewriter — or an instant, non-type-effect
38
+ * reveal that uncovers the whole sentence at once — does not drop the tokens it flies past. Every
39
+ * crossed token fires exactly once, in source order, so the outcome is identical to letting the
40
+ * typewriter reach each one in turn: the image ends in the appearance the *last* crossed token
41
+ * specifies, and each crossed sound effect plays once. Re-visiting an already-fired token within the
42
+ * same reveal is a no-op — it never double-plays a sound effect nor re-writes the expression — which
43
+ * is what keeps a re-mount of an already-revealed line from replaying its effects.
44
+ *
37
45
  * @example
38
46
  * ```ts
39
47
  * // switch Alice's portrait to "angry" the moment the typewriter reaches this point
@@ -12,8 +12,12 @@ export type DarknessOptions = {
12
12
  easing?: TransformDefinitions.EasingDefinition;
13
13
  };
14
14
  /**
15
- * Darken the original image and fade in the target image at the same time.
16
- * Internal: drives `image.darken(x, duration)`.
15
+ * A brightness-dim transition: swaps to the incoming image and animates its
16
+ * brightness from `1 - from` to `1 - to` (darkness `0` leaves it untouched, `1`
17
+ * drives it fully black), replacing the outgoing image at once.
18
+ *
19
+ * This is what backs `image.darken(amount, duration)` — darkening an image in
20
+ * place is expressed as a transition from its current darkness to the new one.
17
21
  */
18
22
  export declare class Darkness extends ImageTransition<AnimationType> {
19
23
  private from;
@@ -14,16 +14,25 @@ export type PushOptions = {
14
14
  * image slides out the opposite way, as if the camera panned.
15
15
  *
16
16
  * The offset is applied via the independent CSS `translate` property (not
17
- * `transform`) in viewport units. That composes additively with the layer's
18
- * base positioning instead of overriding it, and is the identity at offset `0`,
19
- * so neither image jumps at the start/end of the slide.
17
+ * `transform`) in **percentages of the layer's own size**. That composes
18
+ * additively with the layer's base positioning instead of overriding it, and is
19
+ * the identity at offset `0`, so neither image jumps at the start/end of the slide.
20
+ *
21
+ * Percentages — not viewport units — matter here: whichever element this drives
22
+ * lives inside the letterboxed stage box. On the layered render path that element
23
+ * is the transition stack wrapper (`inset: 0`, see `Image.tsx` `stackStyle`); on the
24
+ * non-layered path the style is applied to the `<img>` itself. A `100vw`/`100vh`
25
+ * travel is measured against the *window*, so whenever the window aspect differs
26
+ * from the design aspect the slide overshoots the stage and exposes the backdrop
27
+ * mid-transition. `100%` is measured against that element, so either way a full
28
+ * slide lands exactly one stage width/height away regardless of window shape.
20
29
  */
21
30
  export declare class Push extends ImageTransition<AnimationType> {
22
31
  private duration;
23
32
  private direction;
24
33
  private easing?;
25
34
  constructor(options: PushOptions);
26
- private axisUnit;
35
+ private axisSign;
27
36
  private translate;
28
37
  createTask(): TransitionTask<HTMLImageElement, AnimationType>;
29
38
  copy(): Push;
@@ -18,6 +18,13 @@ type DialogStateConfig = {
18
18
  evaluatedWords: Word<Pausing | string | TextEvent>[];
19
19
  gameState: GameState;
20
20
  suppressInitialAnimation?: boolean;
21
+ /**
22
+ * Persistent per-reveal text-event fire guard. When supplied (NVL entries pass the set stored on
23
+ * their long-lived {@link NvlDialogEntry}), a re-mount of the same line reuses it and so replays
24
+ * neither the sound effects nor the stale expression. Left undefined for ADV, whose dialog state
25
+ * is already memoized per action and falls back to a per-run set.
26
+ */
27
+ firedTextEvents?: Set<TextEvent>;
21
28
  };
22
29
  export declare class DialogState {
23
30
  static Events: {
@@ -1,5 +1,7 @@
1
1
  import type { GameState } from "../../gameState";
2
2
  import type { TextEvent } from "../../../nlcore/elements/character/textEvent";
3
+ import type { Word } from "../../../nlcore/elements/character/word";
4
+ import type { Pausing } from "../../../nlcore/elements/character/pause";
3
5
  /**
4
6
  * Apply a {@link TextEvent}'s effect directly to element state — no transition, no action history,
5
7
  * no stack model. This is the "reveal semantics" seam: the typewriter calls it the moment it
@@ -17,3 +19,15 @@ export declare function dispatchTextEvent(event: TextEvent, state: GameState): v
17
19
  * a replay of the same `say` — starts with an empty set and fires again (contract 4).
18
20
  */
19
21
  export declare function fireTextEventOnce(event: TextEvent, fired: Set<TextEvent>, state: GameState): void;
22
+ /**
23
+ * Land the final state of an instantly-revealed sentence: every {@link TextEvent} token in `words`
24
+ * fires once, in source order — the same "final state" a typewriter skip produces (contract 3).
25
+ *
26
+ * `fired` is the persistent per-reveal guard. Pass the SAME set across re-mounts of one dialog line
27
+ * (e.g. an NVL entry re-keyed on a phase/active change, or the whole container re-mounting) so the
28
+ * re-mount replays neither the sound effects nor the — now stale — expression writes. A genuinely
29
+ * fresh reveal (a new line, or a `say` re-evaluated on load) passes its own empty set and fires
30
+ * again, preserving replay safety (contract 4). Returns the tokens that actually fired this call
31
+ * (empty on a guarded re-mount), for tests.
32
+ */
33
+ export declare function fireInstantRevealEvents(words: readonly Word<string | Pausing | TextEvent>[], fired: Set<TextEvent>, state: GameState): TextEvent[];
@@ -10,6 +10,7 @@ import { Storable } from "../nlcore/elements/persistent/storable";
10
10
  import { Game } from "../nlcore/game";
11
11
  import { Clickable, MenuElement, TextElement } from "./gameState.type";
12
12
  import { Sentence } from "../nlcore/elements/character/sentence";
13
+ import type { TextEvent } from "../nlcore/elements/character/textEvent";
13
14
  import { type SceneSnapshot } from "../nlcore/action/actions/sceneAction";
14
15
  import { Logger } from "../../util/logger";
15
16
  import { Story } from "../nlcore/elements/story";
@@ -36,6 +37,13 @@ export type NvlDialogEntry = {
36
37
  character: Character | null;
37
38
  sentence: Sentence;
38
39
  text: string;
40
+ /**
41
+ * Runtime-only, per-line text-event fire guard. Lives on the long-lived entry (not the React
42
+ * dialog state, which is re-created on every re-mount) so the tokens a line reveals fire once and
43
+ * a re-mount replays nothing. Absent from {@link NvlDialogEntryData}, so a load starts a fresh
44
+ * reveal that fires again (replay safety). See {@link fireInstantRevealEvents}.
45
+ */
46
+ firedTextEvents?: Set<TextEvent>;
39
47
  };
40
48
  export type NvlDialogPhase = "idle" | "typing" | "awaitAdvance";
41
49
  /**