@patterkit/runtime 0.17.0 → 0.18.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/dist/index.d.cts CHANGED
@@ -684,10 +684,13 @@ declare class Flow {
684
684
  private activeSnippet;
685
685
  private beatIndex;
686
686
  private pendingChoice;
687
- /** When `replayPromptOnChoose`, the chosen option's prompt beat to deliver before its content. */
688
- private pendingPromptBeat;
689
- /** The chosen option that owns `pendingPromptBeat`, so a save taken between choose() and the next
690
- * advance() can re-derive the prompt on load (the beat isn't otherwise reachable by id). */
687
+ /** When `replayPromptOnChoose`, the chosen option's AUTHORED prompt to deliver before its content: its
688
+ * beat (id, gameData, tags) and the prompt exactly as the choice showed it (text and speaker fields).
689
+ * `shown` is missing only after loading a save written before saves carried it: the beat is then
690
+ * resolved when it is delivered, as it always was. */
691
+ private pendingPrompt;
692
+ /** The chosen option that owns `pendingPrompt`, so a save taken between choose() and the next
693
+ * advance() can find the prompt beat again on load (it isn't otherwise reachable by id). */
691
694
  private pendingPromptOwnerId;
692
695
  private selectors;
693
696
  /** Per-node entry counts for this flow (node id -> times entered). */
@@ -907,6 +910,11 @@ declare class Flow {
907
910
  * option's first content line. NO look-ahead. Undefined only when even that is absent.
908
911
  */
909
912
  private promptFor;
913
+ /** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
914
+ private authoredPromptOf;
915
+ /** A replayed prompt as a step: the beat's id, gameData, and tags, and the text and speaker fields the
916
+ * choice showed. */
917
+ private promptResult;
910
918
  /** The prompt BEAT of an option: the Option group's `prompt`, else (tolerance) its first content line. */
911
919
  private promptBeatOf;
912
920
  /** The first snippet with a line/text beat within a child list, depth-first in authored order. */
package/dist/index.d.ts CHANGED
@@ -684,10 +684,13 @@ declare class Flow {
684
684
  private activeSnippet;
685
685
  private beatIndex;
686
686
  private pendingChoice;
687
- /** When `replayPromptOnChoose`, the chosen option's prompt beat to deliver before its content. */
688
- private pendingPromptBeat;
689
- /** The chosen option that owns `pendingPromptBeat`, so a save taken between choose() and the next
690
- * advance() can re-derive the prompt on load (the beat isn't otherwise reachable by id). */
687
+ /** When `replayPromptOnChoose`, the chosen option's AUTHORED prompt to deliver before its content: its
688
+ * beat (id, gameData, tags) and the prompt exactly as the choice showed it (text and speaker fields).
689
+ * `shown` is missing only after loading a save written before saves carried it: the beat is then
690
+ * resolved when it is delivered, as it always was. */
691
+ private pendingPrompt;
692
+ /** The chosen option that owns `pendingPrompt`, so a save taken between choose() and the next
693
+ * advance() can find the prompt beat again on load (it isn't otherwise reachable by id). */
691
694
  private pendingPromptOwnerId;
692
695
  private selectors;
693
696
  /** Per-node entry counts for this flow (node id -> times entered). */
@@ -907,6 +910,11 @@ declare class Flow {
907
910
  * option's first content line. NO look-ahead. Undefined only when even that is absent.
908
911
  */
909
912
  private promptFor;
913
+ /** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
914
+ private authoredPromptOf;
915
+ /** A replayed prompt as a step: the beat's id, gameData, and tags, and the text and speaker fields the
916
+ * choice showed. */
917
+ private promptResult;
910
918
  /** The prompt BEAT of an option: the Option group's `prompt`, else (tolerance) its first content line. */
911
919
  private promptBeatOf;
912
920
  /** The first snippet with a line/text beat within a child list, depth-first in authored order. */
package/dist/index.js CHANGED
@@ -843,10 +843,13 @@ var Flow = class {
843
843
  activeSnippet = null;
844
844
  beatIndex = 0;
845
845
  pendingChoice = null;
846
- /** When `replayPromptOnChoose`, the chosen option's prompt beat to deliver before its content. */
847
- pendingPromptBeat = null;
848
- /** The chosen option that owns `pendingPromptBeat`, so a save taken between choose() and the next
849
- * advance() can re-derive the prompt on load (the beat isn't otherwise reachable by id). */
846
+ /** When `replayPromptOnChoose`, the chosen option's AUTHORED prompt to deliver before its content: its
847
+ * beat (id, gameData, tags) and the prompt exactly as the choice showed it (text and speaker fields).
848
+ * `shown` is missing only after loading a save written before saves carried it: the beat is then
849
+ * resolved when it is delivered, as it always was. */
850
+ pendingPrompt = null;
851
+ /** The chosen option that owns `pendingPrompt`, so a save taken between choose() and the next
852
+ * advance() can find the prompt beat again on load (it isn't otherwise reachable by id). */
850
853
  pendingPromptOwnerId = null;
851
854
  selectors = /* @__PURE__ */ new Map();
852
855
  /** Per-node entry counts for this flow (node id -> times entered). */
@@ -1008,7 +1011,7 @@ var Flow = class {
1008
1011
  if (scene === "END") {
1009
1012
  this.started = true;
1010
1013
  this.pendingChoice = null;
1011
- this.pendingPromptBeat = null;
1014
+ this.pendingPrompt = null;
1012
1015
  this.pendingPromptOwnerId = null;
1013
1016
  this.activeSnippet = null;
1014
1017
  this.beatIndex = 0;
@@ -1028,7 +1031,7 @@ var Flow = class {
1028
1031
  return true;
1029
1032
  }
1030
1033
  this.pendingChoice = null;
1031
- this.pendingPromptBeat = null;
1034
+ this.pendingPrompt = null;
1032
1035
  this.pendingPromptOwnerId = null;
1033
1036
  this.activeSnippet = null;
1034
1037
  this.beatIndex = 0;
@@ -1055,7 +1058,7 @@ var Flow = class {
1055
1058
  this.activeSnippet = null;
1056
1059
  this.beatIndex = 0;
1057
1060
  this.pendingChoice = null;
1058
- this.pendingPromptBeat = null;
1061
+ this.pendingPrompt = null;
1059
1062
  this.pendingPromptOwnerId = null;
1060
1063
  }
1061
1064
  /** Inside a checkpoint, the first change to this flow records its cursor and PRNG, so a rollback can
@@ -1065,10 +1068,10 @@ var Flow = class {
1065
1068
  const journal = this.host.journal;
1066
1069
  if (!journal || journal.flows.has(this)) return;
1067
1070
  journal.flows.add(this);
1068
- const { rngState, started, flowEnded, currentSceneId, activeSnippet, beatIndex, pendingChoice, pendingPromptBeat, pendingPromptOwnerId } = this;
1071
+ const { rngState, started, flowEnded, currentSceneId, activeSnippet, beatIndex, pendingChoice, pendingPrompt, pendingPromptOwnerId } = this;
1069
1072
  const stack = this.stack.map((f) => ({ ...f }));
1070
1073
  journal.undo.push(() => {
1071
- Object.assign(this, { rngState, started, flowEnded, currentSceneId, activeSnippet, beatIndex, pendingChoice, pendingPromptBeat, pendingPromptOwnerId, stack });
1074
+ Object.assign(this, { rngState, started, flowEnded, currentSceneId, activeSnippet, beatIndex, pendingChoice, pendingPrompt, pendingPromptOwnerId, stack });
1072
1075
  });
1073
1076
  }
1074
1077
  /** True once the engine has closed this flow (closed, dropped by `reset()`, or replaced by name). */
@@ -1086,11 +1089,11 @@ var Flow = class {
1086
1089
  if (this.closed) return { type: "end" };
1087
1090
  if (!this.started) throw new Error("flow has not been started");
1088
1091
  this.touch();
1089
- if (this.pendingPromptBeat) {
1090
- const b = this.pendingPromptBeat;
1091
- this.pendingPromptBeat = null;
1092
+ if (this.pendingPrompt) {
1093
+ const p = this.pendingPrompt;
1094
+ this.pendingPrompt = null;
1092
1095
  this.pendingPromptOwnerId = null;
1093
- return this.beatResult(b);
1096
+ return p.shown ? this.promptResult(p.beat, p.shown) : this.beatResult(p.beat);
1094
1097
  }
1095
1098
  this.settle();
1096
1099
  if (this.flowEnded) return { type: "end" };
@@ -1209,8 +1212,9 @@ var Flow = class {
1209
1212
  const node = choice.byId.get(id);
1210
1213
  this.emit({ type: "chose", group: choice.groupId, option: id });
1211
1214
  this.pendingChoice = null;
1212
- this.pendingPromptBeat = this.host.replayPromptOnChoose ? this.promptBeatOf(node) ?? null : null;
1213
- this.pendingPromptOwnerId = this.pendingPromptBeat ? node.id : null;
1215
+ const authored = this.host.replayPromptOnChoose ? this.authoredPromptOf(node) : void 0;
1216
+ this.pendingPrompt = authored && option.prompt ? { beat: authored, shown: { ...option.prompt } } : null;
1217
+ this.pendingPromptOwnerId = this.pendingPrompt ? node.id : null;
1214
1218
  this.enterChild(node);
1215
1219
  }
1216
1220
  isEnded() {
@@ -1276,6 +1280,8 @@ var Flow = class {
1276
1280
  beatIndex: this.beatIndex,
1277
1281
  pendingChoice: this.pendingChoice ? { groupId: this.pendingChoice.groupId, options: this.pendingChoice.options.map((o) => ({ ...o })) } : null,
1278
1282
  pendingPromptOwnerId: this.pendingPromptOwnerId,
1283
+ // The prompt still to be replayed, as the choice showed it: present only in the choose -> advance window.
1284
+ ...this.pendingPrompt?.shown ? { pendingPrompt: savedPrompt(this.pendingPrompt.shown) } : {},
1279
1285
  selectors: serialiseSelectors(this.selectors)
1280
1286
  }
1281
1287
  };
@@ -1326,12 +1332,13 @@ var Flow = class {
1326
1332
  }
1327
1333
  if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };
1328
1334
  }
1329
- this.pendingPromptBeat = null;
1335
+ this.pendingPrompt = null;
1330
1336
  this.pendingPromptOwnerId = c.pendingPromptOwnerId ?? null;
1331
1337
  if (this.pendingPromptOwnerId) {
1332
1338
  const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
1333
- this.pendingPromptBeat = owner ? this.promptBeatOf(owner) ?? null : null;
1334
- if (!this.pendingPromptBeat) this.pendingPromptOwnerId = null;
1339
+ const beat = owner ? this.authoredPromptOf(owner) : void 0;
1340
+ if (beat) this.pendingPrompt = { beat, ...c.pendingPrompt ? { shown: { ...c.pendingPrompt } } : {} };
1341
+ else this.pendingPromptOwnerId = null;
1335
1342
  }
1336
1343
  }
1337
1344
  // -- Scene / block / node entry ------------------------------------------
@@ -1726,6 +1733,27 @@ var Flow = class {
1726
1733
  const text = this.interpolate(this.resolveString(beat.id));
1727
1734
  return beat.kind === "line" ? { kind: "line", text: this.captionLine(text), character: beat.character, characterName: this.resolveCharacterName(beat.character), direction: beat.direction } : { kind: "text", text };
1728
1735
  }
1736
+ /** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
1737
+ authoredPromptOf(node) {
1738
+ return node.type === "group" ? node.prompt : void 0;
1739
+ }
1740
+ /** A replayed prompt as a step: the beat's id, gameData, and tags, and the text and speaker fields the
1741
+ * choice showed. */
1742
+ promptResult(beat, shown) {
1743
+ const tags = this.host.tagIndex.get(beat.id);
1744
+ const withTags = tags && tags.length ? { tags } : {};
1745
+ if (shown.kind === "text") return { type: "text", id: beat.id, text: shown.text, gameData: beat.gameData, ...withTags };
1746
+ return {
1747
+ type: "line",
1748
+ id: beat.id,
1749
+ text: shown.text,
1750
+ character: shown.character,
1751
+ characterName: shown.characterName,
1752
+ direction: shown.direction,
1753
+ gameData: beat.gameData,
1754
+ ...withTags
1755
+ };
1756
+ }
1729
1757
  /** The prompt BEAT of an option: the Option group's `prompt`, else (tolerance) its first content line. */
1730
1758
  promptBeatOf(node) {
1731
1759
  if (node.type === "group" && node.prompt) return node.prompt;
@@ -1849,6 +1877,13 @@ function collectCast(nodes, out) {
1849
1877
  function gameDataField(gameData) {
1850
1878
  return gameData && Object.keys(gameData).length ? { gameData } : {};
1851
1879
  }
1880
+ function savedPrompt(p) {
1881
+ const out = { kind: p.kind, text: p.text };
1882
+ if (p.character !== void 0) out.character = p.character;
1883
+ if (p.characterName !== void 0) out.characterName = p.characterName;
1884
+ if (p.direction !== void 0) out.direction = p.direction;
1885
+ return out;
1886
+ }
1852
1887
  function serialiseSelectors(map) {
1853
1888
  const out = {};
1854
1889
  for (const [id, st] of map) {