@patterkit/runtime 0.22.0 → 0.23.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
@@ -2,7 +2,13 @@ import { ScalarValue } from '@wildwinter/expr';
2
2
  import { ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, ScopeResolver, PropertyRow } from '@wildwinter/scoperegistry';
3
3
  export { PropertyRow } from '@wildwinter/scoperegistry';
4
4
  import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
5
- export { Bundle, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
5
+ export { Bundle, DEFAULT_PAD_AFTER, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
6
+
7
+ /** A beat's own `padAfter` (when it sets one) and its resolved one (always). */
8
+ interface BeatPad {
9
+ own?: number;
10
+ resolved: number;
11
+ }
6
12
 
7
13
  type SelectableNode = CompiledGroup | CompiledSnippet;
8
14
 
@@ -16,12 +22,14 @@ type StepResult = {
16
22
  direction?: string;
17
23
  qualifier?: string;
18
24
  qualifierName?: string;
25
+ padAfter: number;
19
26
  gameData?: GameData;
20
27
  tags?: string[];
21
28
  } | {
22
29
  type: "text";
23
30
  id: string;
24
31
  text: string;
32
+ padAfter: number;
25
33
  gameData?: GameData;
26
34
  tags?: string[];
27
35
  } | {
@@ -50,6 +58,11 @@ interface BeatInfo {
50
58
  qualifier?: string;
51
59
  /** The qualifier's shown name (source locale), e.g. `V.O.`. */
52
60
  qualifierName?: string;
61
+ /** The pause after this beat, in seconds, resolved from its own value and the defaults above it (line and
62
+ * text only; see line padding). What a delivered step carries. */
63
+ padAfter?: number;
64
+ /** The beat's own `padAfter`, when it sets one (line and text only). */
65
+ ownPadAfter?: number;
53
66
  /** Source text, un-interpolated (line / text). Omitted for gameEvent and IDs-only bundles. */
54
67
  text?: string;
55
68
  /** Author gameData overrides on this beat (raw, as the step carries them). Omitted when empty. */
@@ -120,6 +133,9 @@ interface ChoicePrompt {
120
133
  /** The speaker qualifier's `gameId` (`vo`), and its resolved shown name (locale-aware). */
121
134
  qualifier?: string;
122
135
  qualifierName?: string;
136
+ /** The prompt's resolved pause, in seconds: when it is spoken (by `replayPromptOnChoose`, or by a game that
137
+ * voices it), the wait before the reply, negative to cut in on the question (line padding). */
138
+ padAfter: number;
123
139
  }
124
140
  /** A single option of a pending `choice` group. */
125
141
  interface ChoiceOption {
@@ -374,6 +390,8 @@ interface FlowHost {
374
390
  blockGameIdToId: Map<string, Map<string, string>>;
375
391
  /** Author tags (#215): node id -> accumulated tags (own + every ancestor's, deduped). Built once. */
376
392
  tagIndex: Map<string, string[]>;
393
+ /** Every line and text beat's resolved pause (line padding), precomputed per bundle (pads.ts). */
394
+ padIndex: Map<string, BeatPad>;
377
395
  /** The game's one registry: `@patter` (the SHARED globals), host scopes, every instance bag. */
378
396
  registry: ScopeRegistry;
379
397
  /** True when the engine made the registry (a standalone game): `saveGame()` then carries its values. */
@@ -1005,6 +1023,12 @@ declare class Flow {
1005
1023
  * locale, else the authoring `displayName`. Undefined when the character has no display name at all
1006
1024
  * (the host falls back to the `character` token itself). */
1007
1025
  private resolveCharacterName;
1026
+ /** The resolved pause of the beat an option shows as its prompt (its authored prompt, else its first content
1027
+ * line), for a saved option that predates prompts carrying one. */
1028
+ private promptPadOf;
1029
+ /** A line or text beat's resolved pause (line padding): its own `padAfter`, else the nearest default above
1030
+ * it, else the project's, else the built-in one; clamped to zero on a snippet's last line. */
1031
+ private padOf;
1008
1032
  /** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
1009
1033
  * active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
1010
1034
  * maps the `qualifier` gameId itself). */
package/dist/index.d.ts CHANGED
@@ -2,7 +2,13 @@ import { ScalarValue } from '@wildwinter/expr';
2
2
  import { ScopeRegistry, PropertyBag, ScopeDeclaration, LogMount, ScopeResolver, PropertyRow } from '@wildwinter/scoperegistry';
3
3
  export { PropertyRow } from '@wildwinter/scoperegistry';
4
4
  import { GameData, Bundle, CompiledGroup, CompiledSnippet, CompiledBlock, FlowSnapshot, EngineSave, SaveGame, SaveGameV2, PropertyType, ScalarValue as ScalarValue$1, GameDataNodeKind, GameDataField } from '@patterkit/model';
5
- export { Bundle, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
5
+ export { Bundle, DEFAULT_PAD_AFTER, EngineSave, FlowCursor, FlowSnapshot, SaveEnvelope, SaveGame, SavedChoice, SavedChoiceOption, SavedChoicePrompt, SelectorSnapshot, StackFrame } from '@patterkit/model';
6
+
7
+ /** A beat's own `padAfter` (when it sets one) and its resolved one (always). */
8
+ interface BeatPad {
9
+ own?: number;
10
+ resolved: number;
11
+ }
6
12
 
7
13
  type SelectableNode = CompiledGroup | CompiledSnippet;
8
14
 
@@ -16,12 +22,14 @@ type StepResult = {
16
22
  direction?: string;
17
23
  qualifier?: string;
18
24
  qualifierName?: string;
25
+ padAfter: number;
19
26
  gameData?: GameData;
20
27
  tags?: string[];
21
28
  } | {
22
29
  type: "text";
23
30
  id: string;
24
31
  text: string;
32
+ padAfter: number;
25
33
  gameData?: GameData;
26
34
  tags?: string[];
27
35
  } | {
@@ -50,6 +58,11 @@ interface BeatInfo {
50
58
  qualifier?: string;
51
59
  /** The qualifier's shown name (source locale), e.g. `V.O.`. */
52
60
  qualifierName?: string;
61
+ /** The pause after this beat, in seconds, resolved from its own value and the defaults above it (line and
62
+ * text only; see line padding). What a delivered step carries. */
63
+ padAfter?: number;
64
+ /** The beat's own `padAfter`, when it sets one (line and text only). */
65
+ ownPadAfter?: number;
53
66
  /** Source text, un-interpolated (line / text). Omitted for gameEvent and IDs-only bundles. */
54
67
  text?: string;
55
68
  /** Author gameData overrides on this beat (raw, as the step carries them). Omitted when empty. */
@@ -120,6 +133,9 @@ interface ChoicePrompt {
120
133
  /** The speaker qualifier's `gameId` (`vo`), and its resolved shown name (locale-aware). */
121
134
  qualifier?: string;
122
135
  qualifierName?: string;
136
+ /** The prompt's resolved pause, in seconds: when it is spoken (by `replayPromptOnChoose`, or by a game that
137
+ * voices it), the wait before the reply, negative to cut in on the question (line padding). */
138
+ padAfter: number;
123
139
  }
124
140
  /** A single option of a pending `choice` group. */
125
141
  interface ChoiceOption {
@@ -374,6 +390,8 @@ interface FlowHost {
374
390
  blockGameIdToId: Map<string, Map<string, string>>;
375
391
  /** Author tags (#215): node id -> accumulated tags (own + every ancestor's, deduped). Built once. */
376
392
  tagIndex: Map<string, string[]>;
393
+ /** Every line and text beat's resolved pause (line padding), precomputed per bundle (pads.ts). */
394
+ padIndex: Map<string, BeatPad>;
377
395
  /** The game's one registry: `@patter` (the SHARED globals), host scopes, every instance bag. */
378
396
  registry: ScopeRegistry;
379
397
  /** True when the engine made the registry (a standalone game): `saveGame()` then carries its values. */
@@ -1005,6 +1023,12 @@ declare class Flow {
1005
1023
  * locale, else the authoring `displayName`. Undefined when the character has no display name at all
1006
1024
  * (the host falls back to the `character` token itself). */
1007
1025
  private resolveCharacterName;
1026
+ /** The resolved pause of the beat an option shows as its prompt (its authored prompt, else its first content
1027
+ * line), for a saved option that predates prompts carrying one. */
1028
+ private promptPadOf;
1029
+ /** A line or text beat's resolved pause (line padding): its own `padAfter`, else the nearest default above
1030
+ * it, else the project's, else the built-in one; clamped to zero on a snippet's last line. */
1031
+ private padOf;
1008
1032
  /** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
1009
1033
  * active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
1010
1034
  * maps the `qualifier` gameId itself). */
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { matchedSpecificity as scoreSpecificity } from "@wildwinter/expr-specifi
4
4
  import { ScopeRegistry } from "@wildwinter/scoperegistry";
5
5
  import { defaultFor, PropertyBag } from "@wildwinter/scoperegistry";
6
6
  import { patterDialect, interpolate, splitRef, stripCaptions } from "@patterkit/dialect";
7
- import { walkNodes, effectiveGameId, castStringKey, qualifierStringKey, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from "@patterkit/model";
7
+ import { walkNodes, effectiveGameId, castStringKey, qualifierStringKey, DEFAULT_PAD_AFTER as DEFAULT_PAD_AFTER2, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from "@patterkit/model";
8
8
 
9
9
  // src/tags.ts
10
10
  function dedupe(tags) {
@@ -40,6 +40,45 @@ function buildTagIndex(bundle) {
40
40
  return index;
41
41
  }
42
42
 
43
+ // src/pads.ts
44
+ import { DEFAULT_PAD_AFTER } from "@patterkit/model";
45
+ var spoken = (b) => b.kind === "line" || b.kind === "text";
46
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
47
+ function buildPadIndex(bundle) {
48
+ const index = /* @__PURE__ */ new Map();
49
+ const set = (beat, inherited, clamp) => {
50
+ const own = beat.kind === "gameEvent" ? void 0 : num(beat.padAfter);
51
+ let resolved = own ?? inherited;
52
+ if (clamp && resolved < 0) resolved = 0;
53
+ index.set(beat.id, own !== void 0 ? { own, resolved } : { resolved });
54
+ };
55
+ const visit = (node, inherited) => {
56
+ const def = num(node.padAfterDefault) ?? inherited;
57
+ if (node.type === "group") {
58
+ if (node.prompt) set(node.prompt, def, false);
59
+ for (const child of node.children) visit(child, def);
60
+ return;
61
+ }
62
+ const beats = node.beats ?? [];
63
+ let last = -1;
64
+ beats.forEach((b, i) => {
65
+ if (spoken(b)) last = i;
66
+ });
67
+ beats.forEach((b, i) => {
68
+ if (spoken(b)) set(b, def, i === last);
69
+ });
70
+ };
71
+ const projectDefault = num(bundle.padAfterDefault) ?? DEFAULT_PAD_AFTER;
72
+ for (const scene of Object.values(bundle.scenes)) {
73
+ const sceneDefault = num(scene.padAfterDefault) ?? projectDefault;
74
+ for (const block of scene.blocks) {
75
+ const blockDefault = num(block.padAfterDefault) ?? sceneDefault;
76
+ for (const child of block.children) visit(child, blockDefault);
77
+ }
78
+ }
79
+ return index;
80
+ }
81
+
43
82
  // src/engine.ts
44
83
  import { SAVE_VERSION } from "@patterkit/model";
45
84
  var astCache = /* @__PURE__ */ new WeakMap();
@@ -52,6 +91,15 @@ function tagIndexFor(bundle) {
52
91
  }
53
92
  return index;
54
93
  }
94
+ var padIndexCache = /* @__PURE__ */ new WeakMap();
95
+ function padIndexFor(bundle) {
96
+ let index = padIndexCache.get(bundle);
97
+ if (!index) {
98
+ index = buildPadIndex(bundle);
99
+ padIndexCache.set(bundle, index);
100
+ }
101
+ return index;
102
+ }
55
103
  function tagsOf(host, id) {
56
104
  return host.tagIndex.get(id)?.slice();
57
105
  }
@@ -187,6 +235,7 @@ var Engine = class _Engine {
187
235
  blockGameIdToId: this.blockGameIdToId,
188
236
  // same instances the engine resolves with
189
237
  tagIndex: tagIndexFor(bundle),
238
+ padIndex: padIndexFor(bundle),
190
239
  registry,
191
240
  ownsRegistry,
192
241
  patterBag,
@@ -591,6 +640,9 @@ var Engine = class _Engine {
591
640
  }
592
641
  }
593
642
  if (beat.kind === "line" || beat.kind === "text") {
643
+ const pad = this.host.padIndex.get(beat.id);
644
+ info.padAfter = pad?.resolved ?? DEFAULT_PAD_AFTER2;
645
+ if (pad?.own !== void 0) info.ownPadAfter = pad.own;
594
646
  const source = this.host.defaultStrings[beat.id];
595
647
  if (source !== void 0) info.text = source;
596
648
  }
@@ -1384,7 +1436,8 @@ var Flow = class {
1384
1436
  const node = this.host.nodeIndex.get(o.id);
1385
1437
  if (!node) continue;
1386
1438
  byId.set(o.id, node);
1387
- options.push({ ...o });
1439
+ const { prompt: saved, ...rest } = o;
1440
+ options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node) } } : {} });
1388
1441
  }
1389
1442
  if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };
1390
1443
  }
@@ -1393,7 +1446,7 @@ var Flow = class {
1393
1446
  if (this.pendingPromptOwnerId) {
1394
1447
  const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
1395
1448
  const beat = owner ? this.authoredPromptOf(owner) : void 0;
1396
- if (beat) this.pendingPrompt = { beat, ...c.pendingPrompt ? { shown: { ...c.pendingPrompt } } : {} };
1449
+ if (beat) this.pendingPrompt = { beat, ...c.pendingPrompt ? { shown: { ...c.pendingPrompt, padAfter: c.pendingPrompt.padAfter ?? this.padOf(beat.id) } } : {} };
1397
1450
  else this.pendingPromptOwnerId = null;
1398
1451
  }
1399
1452
  }
@@ -1763,7 +1816,7 @@ var Flow = class {
1763
1816
  case "gameEvent":
1764
1817
  return { type: "gameEvent", id: beat.id, gameData: beat.gameData, ...withTags };
1765
1818
  case "text":
1766
- return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), gameData: beat.gameData, ...withTags };
1819
+ return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
1767
1820
  case "line": {
1768
1821
  const raw = this.resolveString(beat.id);
1769
1822
  const off = !this.host.captionsOn;
@@ -1779,6 +1832,8 @@ var Flow = class {
1779
1832
  direction: silent ? void 0 : beat.direction,
1780
1833
  qualifier: silent ? void 0 : beat.qualifier,
1781
1834
  qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
1835
+ padAfter: this.padOf(beat.id),
1836
+ // a silent line still fires, so it still carries its pause
1782
1837
  gameData: beat.gameData,
1783
1838
  ...withTags
1784
1839
  };
@@ -1817,6 +1872,7 @@ var Flow = class {
1817
1872
  const beat = this.promptBeatOf(node);
1818
1873
  if (!beat) return void 0;
1819
1874
  const text = this.interpolate(this.resolveString(beat.id));
1875
+ const padAfter = this.padOf(beat.id);
1820
1876
  return beat.kind === "line" ? {
1821
1877
  kind: "line",
1822
1878
  text: this.captionLine(text),
@@ -1824,8 +1880,9 @@ var Flow = class {
1824
1880
  characterName: this.resolveCharacterName(beat.character),
1825
1881
  direction: beat.direction,
1826
1882
  qualifier: beat.qualifier,
1827
- qualifierName: this.resolveQualifierName(beat.qualifier)
1828
- } : { kind: "text", text };
1883
+ qualifierName: this.resolveQualifierName(beat.qualifier),
1884
+ padAfter
1885
+ } : { kind: "text", text, padAfter };
1829
1886
  }
1830
1887
  /** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
1831
1888
  authoredPromptOf(node) {
@@ -1836,7 +1893,7 @@ var Flow = class {
1836
1893
  promptResult(beat, shown) {
1837
1894
  const tags = tagsOf(this.host, beat.id);
1838
1895
  const withTags = tags && tags.length ? { tags } : {};
1839
- if (shown.kind === "text") return { type: "text", id: beat.id, text: shown.text, gameData: beat.gameData, ...withTags };
1896
+ if (shown.kind === "text") return { type: "text", id: beat.id, text: shown.text, padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
1840
1897
  return {
1841
1898
  type: "line",
1842
1899
  id: beat.id,
@@ -1846,6 +1903,7 @@ var Flow = class {
1846
1903
  direction: shown.direction,
1847
1904
  qualifier: shown.qualifier,
1848
1905
  qualifierName: shown.qualifierName,
1906
+ padAfter: this.padOf(beat.id),
1849
1907
  gameData: beat.gameData,
1850
1908
  ...withTags
1851
1909
  };
@@ -1882,6 +1940,17 @@ var Flow = class {
1882
1940
  const key = castStringKey(character);
1883
1941
  return this.host.strings[key] ?? this.host.defaultStrings[key] ?? this.host.castDisplay.get(character);
1884
1942
  }
1943
+ /** The resolved pause of the beat an option shows as its prompt (its authored prompt, else its first content
1944
+ * line), for a saved option that predates prompts carrying one. */
1945
+ promptPadOf(node) {
1946
+ const beat = this.promptBeatOf(node);
1947
+ return beat ? this.padOf(beat.id) : DEFAULT_PAD_AFTER2;
1948
+ }
1949
+ /** A line or text beat's resolved pause (line padding): its own `padAfter`, else the nearest default above
1950
+ * it, else the project's, else the built-in one; clamped to zero on a snippet's last line. */
1951
+ padOf(beatId) {
1952
+ return this.host.padIndex.get(beatId)?.resolved ?? DEFAULT_PAD_AFTER2;
1953
+ }
1885
1954
  /** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
1886
1955
  * active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
1887
1956
  * maps the `qualifier` gameId itself). */
@@ -1989,6 +2058,7 @@ function savedPrompt(p) {
1989
2058
  if (p.direction !== void 0) out.direction = p.direction;
1990
2059
  if (p.qualifier !== void 0) out.qualifier = p.qualifier;
1991
2060
  if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
2061
+ if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
1992
2062
  return out;
1993
2063
  }
1994
2064
  function serialiseSelectors(map) {
@@ -2079,6 +2149,9 @@ function truthy(v) {
2079
2149
  return v.length > 0;
2080
2150
  }
2081
2151
 
2152
+ // src/index.ts
2153
+ import { DEFAULT_PAD_AFTER as DEFAULT_PAD_AFTER3 } from "@patterkit/model";
2154
+
2082
2155
  // src/describe.ts
2083
2156
  import { effectiveGameId as effectiveGameId2 } from "@patterkit/model";
2084
2157
  var isShared = (d, scopeDefault) => d.shared ?? scopeDefault;
@@ -2201,6 +2274,7 @@ function effectiveGameData(fields, node) {
2201
2274
  }
2202
2275
  export {
2203
2276
  Checkpoint,
2277
+ DEFAULT_PAD_AFTER3 as DEFAULT_PAD_AFTER,
2204
2278
  Engine,
2205
2279
  Flow,
2206
2280
  buildTagIndex,