narraleaf-react 0.13.1 → 0.14.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.
package/README.md CHANGED
@@ -1,165 +1,165 @@
1
- <picture>
2
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/NarraLeaf/.github/refs/heads/master/doc/banner-md-transparent.png">
3
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/NarraLeaf/.github/refs/heads/master/doc/banner-md-light.png">
4
- <img alt="NarraLeaf Logo" src="https://raw.githubusercontent.com/NarraLeaf/.github/refs/heads/master/doc/banner-md-light.png">
5
- </picture>
6
-
7
- <h1 align="center">NarraLeaf-React</h1>
8
-
9
- <h4 align="center">A React visual novel player framework</h4>
10
-
11
- <p align="center">English | <a href="docs/README.zh-CN.md">简体中文</a></p>
12
-
13
-
14
- ## What is NarraLeaf-React?
15
-
16
- NarraLeaf-React is a lightweight front-end visual novel player.
17
- NL focuses on visual novel playing, so the user interface can be customized very easily.
18
-
19
- It doesn't use any rendering libraries and can be used on any web platform (e.g. Electron)
20
-
21
- ## Why NarraLeaf-React?
22
-
23
- - **Lightweight**: NarraLeaf-React is a front-end framework, and it doesn't use any rendering libraries.
24
- - **Customizable**: You can customize the UI as you like, even replace the whole components.
25
- - **Easy to use**: It is easy to use and has a simple API that is built for developers. Based on OOP principles.
26
-
27
- ### Scripting
28
-
29
- NarraLeaf-React uses TypeScript for all scripting, so you don't have to learn a whole new language to use it.
30
-
31
- It also has a highly abstracted and easy-to-use API, for example:
32
-
33
- ```typescript
34
- import {Character, Menu, Scene, c, b} from "narraleaf-react";
35
- ```
36
-
37
- ```typescript
38
- const scene1 = new Scene("Scene1: Hello World", {
39
- background: "/link/to/background.jpg",
40
- });
41
-
42
- const jS = new Character("John Smith");
43
- const jD = new Character("John Doe");
44
-
45
- scene1.action([
46
- jS`Hello, world!`,
47
- jS`This is my first ${b("NarraLeaf")} story.`,
48
- jS`Start editing ${c("src/story.js", "#00f")} and enjoy the journey!`,
49
-
50
- jD`Also, don't forget to check out the ${c("documentation", "#00f")}!`,
51
-
52
- "By the way, the documentation is available on https://www.narraleaf.com/docs/narraleaf-react",
53
- "You can also visit the website for demo and more information.",
54
-
55
- Menu.prompt("Start the journey")
56
-
57
- .choose("Yes I will!", [
58
- jS`Great! Let's start the journey!`,
59
- jS`You can open issues on GitHub if you have any questions.`
60
- ])
61
-
62
- .choose("No, I'm going to check the documentation", [
63
- jS`Sure! Take your time!`
64
- ])
65
- ]);
66
- ```
67
-
68
- For more information, please visit the [NarraLeaf-React documentation](https://www.narraleaf.com/docs/narraleaf-react).
69
-
70
- ## Get Started
71
-
72
- ### Install
73
-
74
- ```bash
75
- npm install narraleaf-react
76
- ```
77
-
78
- ### Documentation
79
-
80
- - [Introduction](https://www.narraleaf.com/docs/narraleaf-react)
81
- - [Quick Start](https://www.narraleaf.com/docs/narraleaf-react/quick-start)
82
- - [Installation](https://www.narraleaf.com/docs/narraleaf-react/installation)
83
- - [Basic](https://www.narraleaf.com/docs/narraleaf-react/basic)
84
- - [Create a Scene](https://www.narraleaf.com/docs/narraleaf-react/basic/create-scene)
85
- - [Add Actions](https://www.narraleaf.com/docs/narraleaf-react/basic/add-actions)
86
- - [Show Dialog](https://www.narraleaf.com/docs/narraleaf-react/basic/show-dialog)
87
- - [Show Image](https://www.narraleaf.com/docs/narraleaf-react/basic/show-image)
88
- - [Play Story](https://www.narraleaf.com/docs/narraleaf-react/basic/play-story)
89
- - [Make Choices](https://www.narraleaf.com/docs/narraleaf-react/basic/make-choices)
90
- - [Play Sound](https://www.narraleaf.com/docs/narraleaf-react/basic/sound)
91
- - [Store Data](https://www.narraleaf.com/docs/narraleaf-react/basic/store-data)
92
- - [Conditional](https://www.narraleaf.com/docs/narraleaf-react/basic/conditional)
93
- - [Voice](https://www.narraleaf.com/docs/narraleaf-react/basic/voice)
94
- - [Manage Preferences](https://www.narraleaf.com/docs/narraleaf-react/basic/manage-preferences)
95
- - [Solutions](https://www.narraleaf.com/docs/narraleaf-react/solutions)
96
- - [Customizing the Font](https://www.narraleaf.com/docs/narraleaf-react/solutions/font)
97
- - [Migration from Ren'Py](https://www.narraleaf.com/docs/narraleaf-react/solutions/from-renpy)
98
- - [Quick Menu](https://www.narraleaf.com/docs/narraleaf-react/solutions/quick-menu)
99
- - [Dialog Avatar](https://www.narraleaf.com/docs/narraleaf-react/solutions/dialog-avatar)
100
- - [Custom Dialog](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-dialog)
101
- - [Custom NVL Dialog](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-nvl-dialog)
102
- - [Custom Menu](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-menu)
103
- - [Page Overlay Settings](https://www.narraleaf.com/docs/narraleaf-react/solutions/page-overlay-settings)
104
- - [Save System with localStorage](https://www.narraleaf.com/docs/narraleaf-react/solutions/save-system-localstorage)
105
- - [Custom Notification](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-notification)
106
- - [Gallery Service with localStorage](https://www.narraleaf.com/docs/narraleaf-react/solutions/gallery-service-localstorage)
107
- - [Core](https://www.narraleaf.com/docs/narraleaf-react/core)
108
- - [Elements](https://www.narraleaf.com/docs/narraleaf-react/core/elements)
109
- - [Scene](https://www.narraleaf.com/docs/narraleaf-react/core/elements/scene)
110
- - [Character](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character)
111
- - [Sentence](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character/sentence)
112
- - [Word](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character/word)
113
- - [Pause](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character/pause)
114
- - [Image](https://www.narraleaf.com/docs/narraleaf-react/core/elements/image)
115
- - [Sound](https://www.narraleaf.com/docs/narraleaf-react/core/elements/sound)
116
- - [Menu](https://www.narraleaf.com/docs/narraleaf-react/core/elements/menu)
117
- - [Script](https://www.narraleaf.com/docs/narraleaf-react/core/elements/script)
118
- - [Condition](https://www.narraleaf.com/docs/narraleaf-react/core/elements/condition)
119
- - [Control](https://www.narraleaf.com/docs/narraleaf-react/core/elements/control)
120
- - [Text](https://www.narraleaf.com/docs/narraleaf-react/core/elements/text)
121
- - [Persistent](https://www.narraleaf.com/docs/narraleaf-react/core/elements/persistent)
122
- - [Story](https://www.narraleaf.com/docs/narraleaf-react/core/elements/story)
123
- - [Displayable](https://www.narraleaf.com/docs/narraleaf-react/core/elements/displayable)
124
- - [Layer](https://www.narraleaf.com/docs/narraleaf-react/core/elements/layer)
125
- - [Service](https://www.narraleaf.com/docs/narraleaf-react/core/elements/service)
126
- - [Video](https://www.narraleaf.com/docs/narraleaf-react/core/elements/video)
127
- - [Built-in Gallery](https://www.narraleaf.com/docs/narraleaf-react/core/elements/built-in/gallery)
128
- - [Animation](https://www.narraleaf.com/docs/narraleaf-react/core/animation)
129
- - [Transform](https://www.narraleaf.com/docs/narraleaf-react/core/animation/transform)
130
- - [Transitions](https://www.narraleaf.com/docs/narraleaf-react/core/animation/transition)
131
- - [Game](https://www.narraleaf.com/docs/narraleaf-react/core/game)
132
- - [LiveGame](https://www.narraleaf.com/docs/narraleaf-react/core/game/live-game)
133
- - [Storable](https://www.narraleaf.com/docs/narraleaf-react/core/game/storable)
134
- - [Preference](https://www.narraleaf.com/docs/narraleaf-react/core/game/preference/preference)
135
- - [Key Map](https://www.narraleaf.com/docs/narraleaf-react/core/game/key-map)
136
- - [Hooks](https://www.narraleaf.com/docs/narraleaf-react/core/game/hooks)
137
- - [Plugin](https://www.narraleaf.com/docs/narraleaf-react/core/plugin)
138
- - [Utils](https://www.narraleaf.com/docs/narraleaf-react/core/utils)
139
- - [Player](https://www.narraleaf.com/docs/narraleaf-react/player)
140
- - [Player](https://www.narraleaf.com/docs/narraleaf-react/player/player)
141
- - [FixedAspectRatioContainer](https://www.narraleaf.com/docs/narraleaf-react/player/fixed-aspect-ratio-container)
142
- - [GameProviders](https://www.narraleaf.com/docs/narraleaf-react/player/game-providers)
143
- - [Hooks](https://www.narraleaf.com/docs/narraleaf-react/player/hooks)
144
- - [LayoutRouter](https://www.narraleaf.com/docs/narraleaf-react/player/page-router)
145
- - [Dialog](https://www.narraleaf.com/docs/narraleaf-react/player/dialog)
146
- - [Notification](https://www.narraleaf.com/docs/narraleaf-react/player/notification)
147
- - [Menu](https://www.narraleaf.com/docs/narraleaf-react/player/menu)
148
- - [NvlContainer](https://www.narraleaf.com/docs/narraleaf-react/player/nvl-container)
149
- - About
150
- - [License](https://www.narraleaf.com/docs/narraleaf-react/info/license)
151
- - [Incompatible Changes](https://www.narraleaf.com/docs/narraleaf-react/info/incompatible-changes)
152
-
153
- Read more in the [NarraLeaf-React documentation](https://www.narraleaf.com/docs/narraleaf-react).
154
-
155
- ## License
156
-
157
- > NarraLeaf-React is licensed under the MPL-2.0 License.
158
- >
159
- > We updated the license to MPL-2.0 on 2024-9-24.
160
-
161
- ## Contributing
162
-
163
- We welcome all contributions.
164
- If you have any ideas, just open an issue or a pull request.
165
-
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/NarraLeaf/.github/refs/heads/master/doc/banner-md-transparent.png">
3
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/NarraLeaf/.github/refs/heads/master/doc/banner-md-light.png">
4
+ <img alt="NarraLeaf Logo" src="https://raw.githubusercontent.com/NarraLeaf/.github/refs/heads/master/doc/banner-md-light.png">
5
+ </picture>
6
+
7
+ <h1 align="center">NarraLeaf-React</h1>
8
+
9
+ <h4 align="center">A React visual novel player framework</h4>
10
+
11
+ <p align="center">English | <a href="docs/README.zh-CN.md">简体中文</a></p>
12
+
13
+
14
+ ## What is NarraLeaf-React?
15
+
16
+ NarraLeaf-React is a lightweight front-end visual novel player.
17
+ NL focuses on visual novel playing, so the user interface can be customized very easily.
18
+
19
+ It doesn't use any rendering libraries and can be used on any web platform (e.g. Electron)
20
+
21
+ ## Why NarraLeaf-React?
22
+
23
+ - **Lightweight**: NarraLeaf-React is a front-end framework, and it doesn't use any rendering libraries.
24
+ - **Customizable**: You can customize the UI as you like, even replace the whole components.
25
+ - **Easy to use**: It is easy to use and has a simple API that is built for developers. Based on OOP principles.
26
+
27
+ ### Scripting
28
+
29
+ NarraLeaf-React uses TypeScript for all scripting, so you don't have to learn a whole new language to use it.
30
+
31
+ It also has a highly abstracted and easy-to-use API, for example:
32
+
33
+ ```typescript
34
+ import {Character, Menu, Scene, c, b} from "narraleaf-react";
35
+ ```
36
+
37
+ ```typescript
38
+ const scene1 = new Scene("Scene1: Hello World", {
39
+ background: "/link/to/background.jpg",
40
+ });
41
+
42
+ const jS = new Character("John Smith");
43
+ const jD = new Character("John Doe");
44
+
45
+ scene1.action([
46
+ jS`Hello, world!`,
47
+ jS`This is my first ${b("NarraLeaf")} story.`,
48
+ jS`Start editing ${c("src/story.js", "#00f")} and enjoy the journey!`,
49
+
50
+ jD`Also, don't forget to check out the ${c("documentation", "#00f")}!`,
51
+
52
+ "By the way, the documentation is available on https://www.narraleaf.com/docs/narraleaf-react",
53
+ "You can also visit the website for demo and more information.",
54
+
55
+ Menu.prompt("Start the journey")
56
+
57
+ .choose("Yes I will!", [
58
+ jS`Great! Let's start the journey!`,
59
+ jS`You can open issues on GitHub if you have any questions.`
60
+ ])
61
+
62
+ .choose("No, I'm going to check the documentation", [
63
+ jS`Sure! Take your time!`
64
+ ])
65
+ ]);
66
+ ```
67
+
68
+ For more information, please visit the [NarraLeaf-React documentation](https://www.narraleaf.com/docs/narraleaf-react).
69
+
70
+ ## Get Started
71
+
72
+ ### Install
73
+
74
+ ```bash
75
+ npm install narraleaf-react
76
+ ```
77
+
78
+ ### Documentation
79
+
80
+ - [Introduction](https://www.narraleaf.com/docs/narraleaf-react)
81
+ - [Quick Start](https://www.narraleaf.com/docs/narraleaf-react/quick-start)
82
+ - [Installation](https://www.narraleaf.com/docs/narraleaf-react/installation)
83
+ - [Basic](https://www.narraleaf.com/docs/narraleaf-react/basic)
84
+ - [Create a Scene](https://www.narraleaf.com/docs/narraleaf-react/basic/create-scene)
85
+ - [Add Actions](https://www.narraleaf.com/docs/narraleaf-react/basic/add-actions)
86
+ - [Show Dialog](https://www.narraleaf.com/docs/narraleaf-react/basic/show-dialog)
87
+ - [Show Image](https://www.narraleaf.com/docs/narraleaf-react/basic/show-image)
88
+ - [Play Story](https://www.narraleaf.com/docs/narraleaf-react/basic/play-story)
89
+ - [Make Choices](https://www.narraleaf.com/docs/narraleaf-react/basic/make-choices)
90
+ - [Play Sound](https://www.narraleaf.com/docs/narraleaf-react/basic/sound)
91
+ - [Store Data](https://www.narraleaf.com/docs/narraleaf-react/basic/store-data)
92
+ - [Conditional](https://www.narraleaf.com/docs/narraleaf-react/basic/conditional)
93
+ - [Voice](https://www.narraleaf.com/docs/narraleaf-react/basic/voice)
94
+ - [Manage Preferences](https://www.narraleaf.com/docs/narraleaf-react/basic/manage-preferences)
95
+ - [Solutions](https://www.narraleaf.com/docs/narraleaf-react/solutions)
96
+ - [Customizing the Font](https://www.narraleaf.com/docs/narraleaf-react/solutions/font)
97
+ - [Migration from Ren'Py](https://www.narraleaf.com/docs/narraleaf-react/solutions/from-renpy)
98
+ - [Quick Menu](https://www.narraleaf.com/docs/narraleaf-react/solutions/quick-menu)
99
+ - [Dialog Avatar](https://www.narraleaf.com/docs/narraleaf-react/solutions/dialog-avatar)
100
+ - [Custom Dialog](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-dialog)
101
+ - [Custom NVL Dialog](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-nvl-dialog)
102
+ - [Custom Menu](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-menu)
103
+ - [Page Overlay Settings](https://www.narraleaf.com/docs/narraleaf-react/solutions/page-overlay-settings)
104
+ - [Save System with localStorage](https://www.narraleaf.com/docs/narraleaf-react/solutions/save-system-localstorage)
105
+ - [Custom Notification](https://www.narraleaf.com/docs/narraleaf-react/solutions/custom-notification)
106
+ - [Gallery Service with localStorage](https://www.narraleaf.com/docs/narraleaf-react/solutions/gallery-service-localstorage)
107
+ - [Core](https://www.narraleaf.com/docs/narraleaf-react/core)
108
+ - [Elements](https://www.narraleaf.com/docs/narraleaf-react/core/elements)
109
+ - [Scene](https://www.narraleaf.com/docs/narraleaf-react/core/elements/scene)
110
+ - [Character](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character)
111
+ - [Sentence](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character/sentence)
112
+ - [Word](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character/word)
113
+ - [Pause](https://www.narraleaf.com/docs/narraleaf-react/core/elements/character/pause)
114
+ - [Image](https://www.narraleaf.com/docs/narraleaf-react/core/elements/image)
115
+ - [Sound](https://www.narraleaf.com/docs/narraleaf-react/core/elements/sound)
116
+ - [Menu](https://www.narraleaf.com/docs/narraleaf-react/core/elements/menu)
117
+ - [Script](https://www.narraleaf.com/docs/narraleaf-react/core/elements/script)
118
+ - [Condition](https://www.narraleaf.com/docs/narraleaf-react/core/elements/condition)
119
+ - [Control](https://www.narraleaf.com/docs/narraleaf-react/core/elements/control)
120
+ - [Text](https://www.narraleaf.com/docs/narraleaf-react/core/elements/text)
121
+ - [Persistent](https://www.narraleaf.com/docs/narraleaf-react/core/elements/persistent)
122
+ - [Story](https://www.narraleaf.com/docs/narraleaf-react/core/elements/story)
123
+ - [Displayable](https://www.narraleaf.com/docs/narraleaf-react/core/elements/displayable)
124
+ - [Layer](https://www.narraleaf.com/docs/narraleaf-react/core/elements/layer)
125
+ - [Service](https://www.narraleaf.com/docs/narraleaf-react/core/elements/service)
126
+ - [Video](https://www.narraleaf.com/docs/narraleaf-react/core/elements/video)
127
+ - [Built-in Gallery](https://www.narraleaf.com/docs/narraleaf-react/core/elements/built-in/gallery)
128
+ - [Animation](https://www.narraleaf.com/docs/narraleaf-react/core/animation)
129
+ - [Transform](https://www.narraleaf.com/docs/narraleaf-react/core/animation/transform)
130
+ - [Transitions](https://www.narraleaf.com/docs/narraleaf-react/core/animation/transition)
131
+ - [Game](https://www.narraleaf.com/docs/narraleaf-react/core/game)
132
+ - [LiveGame](https://www.narraleaf.com/docs/narraleaf-react/core/game/live-game)
133
+ - [Storable](https://www.narraleaf.com/docs/narraleaf-react/core/game/storable)
134
+ - [Preference](https://www.narraleaf.com/docs/narraleaf-react/core/game/preference/preference)
135
+ - [Key Map](https://www.narraleaf.com/docs/narraleaf-react/core/game/key-map)
136
+ - [Hooks](https://www.narraleaf.com/docs/narraleaf-react/core/game/hooks)
137
+ - [Plugin](https://www.narraleaf.com/docs/narraleaf-react/core/plugin)
138
+ - [Utils](https://www.narraleaf.com/docs/narraleaf-react/core/utils)
139
+ - [Player](https://www.narraleaf.com/docs/narraleaf-react/player)
140
+ - [Player](https://www.narraleaf.com/docs/narraleaf-react/player/player)
141
+ - [FixedAspectRatioContainer](https://www.narraleaf.com/docs/narraleaf-react/player/fixed-aspect-ratio-container)
142
+ - [GameProviders](https://www.narraleaf.com/docs/narraleaf-react/player/game-providers)
143
+ - [Hooks](https://www.narraleaf.com/docs/narraleaf-react/player/hooks)
144
+ - [LayoutRouter](https://www.narraleaf.com/docs/narraleaf-react/player/page-router)
145
+ - [Dialog](https://www.narraleaf.com/docs/narraleaf-react/player/dialog)
146
+ - [Notification](https://www.narraleaf.com/docs/narraleaf-react/player/notification)
147
+ - [Menu](https://www.narraleaf.com/docs/narraleaf-react/player/menu)
148
+ - [NvlContainer](https://www.narraleaf.com/docs/narraleaf-react/player/nvl-container)
149
+ - About
150
+ - [License](https://www.narraleaf.com/docs/narraleaf-react/info/license)
151
+ - [Incompatible Changes](https://www.narraleaf.com/docs/narraleaf-react/info/incompatible-changes)
152
+
153
+ Read more in the [NarraLeaf-React documentation](https://www.narraleaf.com/docs/narraleaf-react).
154
+
155
+ ## License
156
+
157
+ > NarraLeaf-React is licensed under the MPL-2.0 License.
158
+ >
159
+ > We updated the license to MPL-2.0 on 2024-9-24.
160
+
161
+ ## Contributing
162
+
163
+ We welcome all contributions.
164
+ If you have any ideas, just open an issue or a pull request.
165
+
@@ -1,3 +1,4 @@
1
+ import type { SerializedGameHistory, SerializedGameState } from "../gameTypes";
1
2
  import { Action } from "./action";
2
3
  import { ActionHistoryManager } from "./actionHistory";
3
4
  type GameHistoryAction = {
@@ -5,7 +6,7 @@ type GameHistoryAction = {
5
6
  action: Action;
6
7
  isPending?: boolean;
7
8
  };
8
- type GameElementHistory = {
9
+ export type GameElementHistory = {
9
10
  type: "say";
10
11
  text: string;
11
12
  voice: string | null;
@@ -17,6 +18,12 @@ type GameElementHistory = {
17
18
  };
18
19
  export type GameHistory = GameHistoryAction & {
19
20
  element: GameElementHistory;
21
+ /**
22
+ * Self-contained core state captured when this line was reached, used to restore the game
23
+ * back to this backlog line (see {@link LiveGame.restoreToHistory}). Optional because a
24
+ * capture can fail, and legacy in-memory entries created before a snapshot was taken.
25
+ */
26
+ snapshot?: SerializedGameState | null;
20
27
  };
21
28
  export declare class GameHistoryManager {
22
29
  private history;
@@ -24,7 +31,28 @@ export declare class GameHistoryManager {
24
31
  constructor(actionHistoryMgr: ActionHistoryManager);
25
32
  push(action: GameHistory): this;
26
33
  getHistory(): GameHistory[];
34
+ getByToken(token: string): GameHistory | null;
35
+ /**
36
+ * Serialize the whole backlog for persistence (save format v2).
37
+ */
38
+ serialize(): SerializedGameHistory[];
39
+ /**
40
+ * Serialize the backlog up to and including the entry with the given token.
41
+ *
42
+ * Used by restore-to-history to trim the backlog back to the restored line.
43
+ * Returns an empty array if the token is not found.
44
+ */
45
+ serializeUntil(token: string): SerializedGameHistory[];
46
+ /**
47
+ * Rebuild the backlog from persisted entries.
48
+ *
49
+ * Each entry is re-bound to a live {@link Action} through `actionMap`; entries whose action no
50
+ * longer exists (the script changed since the save) are dropped rather than throwing, so a
51
+ * still-loadable save degrades to a shorter backlog instead of failing.
52
+ */
53
+ load(entries: SerializedGameHistory[], actionMap: ReadonlyMap<string, Action>): void;
27
54
  reset(): void;
55
+ private static toSerialized;
28
56
  updateByToken(id: string, handler: (result: GameHistory | null) => void): void;
29
57
  resolvePending(id: string): void;
30
58
  private crossFilter;
@@ -59,6 +59,18 @@ export declare class LiveGame {
59
59
  * - If the id is not provided, it will undo **the last action**
60
60
  */
61
61
  undo(id?: string): void;
62
+ /**
63
+ * Restore the game to a past backlog line.
64
+ *
65
+ * Unlike {@link undo}, this works **after loading a save**: it does not rely on the
66
+ * (non-serializable) undo stack. Every backlog entry carries a self-contained state snapshot,
67
+ * so restoring re-applies that snapshot and trims the backlog back to that line.
68
+ *
69
+ * @param token - the backlog entry token (as returned by {@link getHistory})
70
+ * @returns `true` if the line was restored, `false` if the token is unknown or the entry has
71
+ * no restore snapshot.
72
+ */
73
+ restoreToHistory(token: string): boolean;
62
74
  /**
63
75
  * Notify the player with a message
64
76
  *
@@ -74,6 +86,35 @@ export declare class LiveGame {
74
86
  * Skip the current dialog
75
87
  */
76
88
  skipDialog(): void;
89
+ /**
90
+ * Fast-forward playback to the next menu (or the end of the story).
91
+ *
92
+ * Every line in between is executed for real, so the backlog and its restore snapshots
93
+ * accumulate exactly as in normal play — only faster and silent. Audio is muted for the
94
+ * duration, in-flight transitions are settled immediately, and timed pauses (`Control.sleep`,
95
+ * auto-forward) resolve at once. It stops as soon as a menu is waiting for a choice, so the
96
+ * choice itself is always left to the player.
97
+ *
98
+ * Because history accumulates the whole way, {@link getHistory} and
99
+ * {@link restoreToHistory} cover the fast-forwarded span just like normal play.
100
+ *
101
+ * ```typescript
102
+ * // Jump ahead to the next decision point.
103
+ * await game.getLiveGame().fastForward();
104
+ * ```
105
+ *
106
+ * @param options.until - `"menu"` (default) stops at the next menu; `"end"` runs until the
107
+ * story finishes.
108
+ * @param options.maxSteps - safety bound on the number of advance steps (defaults to the
109
+ * `maxStackModelLoop` config).
110
+ * @returns why it stopped: `"menu"`, `"end"` (the stack drained), or `"maxSteps"`.
111
+ */
112
+ fastForward(options?: {
113
+ until?: "menu" | "end";
114
+ maxSteps?: number;
115
+ }): Promise<{
116
+ reason: "menu" | "end" | "maxSteps";
117
+ }>;
77
118
  private assertScreenshot;
78
119
  /**
79
120
  * Capture the game screenshot, will only include the player element
@@ -7,8 +7,17 @@ import { PlayerStateData } from "../player/gameState";
7
7
  import { GuardConfig } from "../player/guard";
8
8
  import React from "react";
9
9
  import { StackModel, StackModelRawData } from "./action/stackModel";
10
+ import type { GameElementHistory } from "./action/gameHistory";
10
11
  import { MenuComponent, NotificationComponent, NvlDialogComponent, SayComponent } from "./common/player";
11
12
  import { LiveGameEventToken } from "./types";
13
+ /**
14
+ * Current save format version.
15
+ *
16
+ * - v1 (undefined on the save): core resume state only, no backlog history.
17
+ * - v2: adds `game.history`, a full backlog where every entry carries a self-contained
18
+ * restore snapshot, so loading a save keeps the backlog and any past line can be restored.
19
+ */
20
+ export declare const SAVE_FORMAT_VERSION = 2;
12
21
  export interface SavedGameMetaData {
13
22
  /**
14
23
  * The timestamp of when the game was created
@@ -34,21 +43,63 @@ export interface SavedGameMetaData {
34
43
  * The hash of the story is used to check whether the stories are compatible.
35
44
  */
36
45
  storyHash: string;
46
+ /**
47
+ * The save format version (see {@link SAVE_FORMAT_VERSION}).
48
+ *
49
+ * Absent on legacy (v1) saves written before backlog history existed.
50
+ */
51
+ version?: number;
52
+ }
53
+ /**
54
+ * The core, resumable game state — everything needed to restore the game to a point,
55
+ * **without** the backlog history.
56
+ *
57
+ * This is the unit captured per backlog entry (see {@link SerializedGameHistory}); it is
58
+ * deliberately history-free so that per-entry snapshots do not nest the whole backlog inside
59
+ * themselves (which would make saves grow exponentially).
60
+ */
61
+ export interface SerializedGameState {
62
+ store: {
63
+ [key: string]: SerializedNamespaceData;
64
+ };
65
+ elementStates: RawData<ElementStateRaw>[];
66
+ stage: PlayerStateData;
67
+ services: {
68
+ [key: string]: unknown;
69
+ };
70
+ stackModel: StackModelRawData;
71
+ asyncStackModels: StackModelRawData[];
72
+ }
73
+ /**
74
+ * A single persisted backlog line: the rendered say/menu content, a stable action anchor, and a
75
+ * self-contained snapshot that restores the game to exactly this line.
76
+ */
77
+ export interface SerializedGameHistory {
78
+ /**
79
+ * Stable action id anchor (`action.getId()`). Used to re-bind the entry to the live story on
80
+ * load; entries whose action no longer exists (script changed) are dropped from the backlog.
81
+ */
82
+ actionId: string | null;
83
+ element: GameElementHistory;
84
+ isPending?: boolean;
85
+ /**
86
+ * Core game state captured when this line was reached. Restoring it returns the game to this
87
+ * exact line. `null` when a snapshot could not be captured (the line stays visible but is not
88
+ * restorable).
89
+ */
90
+ snapshot: SerializedGameState | null;
37
91
  }
38
92
  export interface SavedGame {
39
93
  name: string;
40
94
  meta: SavedGameMetaData;
41
- game: {
42
- store: {
43
- [key: string]: SerializedNamespaceData;
44
- };
45
- elementStates: RawData<ElementStateRaw>[];
46
- stage: PlayerStateData;
47
- services: {
48
- [key: string]: unknown;
49
- };
50
- stackModel: StackModelRawData;
51
- asyncStackModels: StackModelRawData[];
95
+ game: SerializedGameState & {
96
+ /**
97
+ * Full backlog with per-entry restore snapshots (save format v2+).
98
+ *
99
+ * Absent on legacy saves; a missing/empty history simply means loading starts with an
100
+ * empty backlog, exactly as before this feature existed.
101
+ */
102
+ history?: SerializedGameHistory[];
52
103
  };
53
104
  }
54
105
  export type GameConfig = {
@@ -399,7 +450,7 @@ export type GamePreference = {
399
450
  * This is used to prevent the game from skipping actions too quickly when the player presses the skip key.
400
451
  *
401
452
  * Set to 0 to skip actions immediately when the player presses the skip key.
402
- * @default 500
453
+ * @default 0
403
454
  */
404
455
  skipDelay: number;
405
456
  /**
@@ -3,3 +3,5 @@ export type ImageEvents = {
3
3
  "event:image.onLoad": [];
4
4
  };
5
5
  export declare function stackStyle(darkness: number): React.CSSProperties;
6
+ declare const Image: React.MemoExoticComponent<typeof ImageComponent>;
7
+ export default Image;
@@ -178,6 +178,7 @@ export declare class GameState {
178
178
  private stageClickBuffer;
179
179
  private readonly nvlAdvanceWaiters;
180
180
  private advDialogState;
181
+ private _fastForwarding;
181
182
  constructor(game: Game, stage: StageUtils);
182
183
  get deps(): number;
183
184
  addVideo(video: Video): this;
@@ -26,6 +26,7 @@ export type ExposedState = {
26
26
  [ExposedStateType.image]: {
27
27
  createWearable: (wearable: Image) => void;
28
28
  disposeWearable: (wearable: Image) => void;
29
+ flush: () => void;
29
30
  events: EventDispatcher<ImageEvents>;
30
31
  initDisplayable: (onResolve: () => void) => Timeline;
31
32
  applyTransform: (transform: Transform, onResolve: () => void) => Timeline;