@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/CHANGELOG.md CHANGED
@@ -33,6 +33,20 @@ version number always means the same runtime behaviour. This package is versione
33
33
 
34
34
  ## [Unreleased]
35
35
 
36
+ ## [0.23.0] - 2026-10-10
37
+
38
+ ### Added
39
+
40
+ - **Line padding**: the writer's pause after a line, in seconds. A `line` or `text` step carries `padAfter`,
41
+ resolved from the beat's own value, else the nearest `padAfterDefault` above it (its snippet, each group
42
+ around it, innermost first, its block, its scene), else the project's, else the built-in 0.6. Negative is a
43
+ cut-in, the next line (or game event) starting that long before this one ends; it is clamped to zero only on
44
+ a snippet's last line or text beat, where nothing certain follows. A choice option's `prompt` carries its
45
+ `padAfter` too, resolved through its option and never clamped, for a game that voices the prompts itself.
46
+ The outline and beat sequence carry `padAfter` and, when the beat sets its own, `ownPadAfter`.
47
+ `DEFAULT_PAD_AFTER` (0.6) is exported. What to do with it is the game's: the docs give the timing
48
+ rules a writer expects.
49
+
36
50
  ## [0.22.0] - 2026-10-10
37
51
 
38
52
  ### Added
package/README.md CHANGED
@@ -44,6 +44,13 @@ for (;;) {
44
44
  }
45
45
  ```
46
46
 
47
+ A `line` step carries `id`, `text`, `character`, `characterName`, `direction`, and, when the line has a
48
+ speaker qualifier, `qualifier` (its `gameId`, `os`) and `qualifierName` (its shown name, `O.S.`). Line and
49
+ text steps both carry `padAfter`, the writer's pause after the beat in seconds, already resolved; a negative
50
+ one means the next line cuts in. Line, text, and game event steps can carry `gameData` and `tags`. The
51
+ [step shapes](https://patterkit.dev/play/engine/#step-shapes) give the full list and the timing rules a game
52
+ should follow.
53
+
47
54
  `new Engine(bundle, options)` takes `{ rng?, seed?, locale?, hostScopes?, registry?, replayPromptOnChoose?, closedCaptions? }`:
48
55
  - `hostScopes` binds the scopes your game owns, by token, each `{ get(name), set?(name, value) }`: for World
49
56
  Properties, `hostScopes: { world: { get, set } }`. Leave `world` unbound and the runtime self-backs `@world`
@@ -60,8 +67,8 @@ state, visit counts, and PRNG are untouched - so a game's "language" setting can
60
67
  rebuilding the engine or losing the player's place.
61
68
 
62
69
  The above is the **Embedded** build (strings ship inside the `.patterc`). An **IDs-only** build ships no
63
- strings: `step.text` is the beat **ID** and `step.characterName` is omitted - your game localises the IDs in
64
- its own system, then applies `{@ref}` replacement with **`flow.interpolate(yourString)`**. See the
70
+ strings: `step.text` is the beat **ID**, and `step.characterName` and `step.qualifierName` are omitted, so
71
+ your game localises the IDs in its own system, then applies `{@ref}` replacement with **`flow.interpolate(yourString)`**. See the
65
72
  [Localisation guide](https://patterkit.dev/play/localisation/) for both modes across all four runtimes.
66
73
 
67
74
  ## Drop-in (`<script>`)
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Checkpoint: () => Checkpoint,
24
+ DEFAULT_PAD_AFTER: () => import_model5.DEFAULT_PAD_AFTER,
24
25
  Engine: () => Engine,
25
26
  Flow: () => Flow,
26
27
  buildTagIndex: () => buildTagIndex,
@@ -37,7 +38,7 @@ var import_expr_specificity = require("@wildwinter/expr-specificity");
37
38
  var import_scoperegistry = require("@wildwinter/scoperegistry");
38
39
  var import_scoperegistry2 = require("@wildwinter/scoperegistry");
39
40
  var import_dialect = require("@patterkit/dialect");
40
- var import_model = require("@patterkit/model");
41
+ var import_model2 = require("@patterkit/model");
41
42
 
42
43
  // src/tags.ts
43
44
  function dedupe(tags) {
@@ -73,8 +74,47 @@ function buildTagIndex(bundle) {
73
74
  return index;
74
75
  }
75
76
 
77
+ // src/pads.ts
78
+ var import_model = require("@patterkit/model");
79
+ var spoken = (b) => b.kind === "line" || b.kind === "text";
80
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
81
+ function buildPadIndex(bundle) {
82
+ const index = /* @__PURE__ */ new Map();
83
+ const set = (beat, inherited, clamp) => {
84
+ const own = beat.kind === "gameEvent" ? void 0 : num(beat.padAfter);
85
+ let resolved = own ?? inherited;
86
+ if (clamp && resolved < 0) resolved = 0;
87
+ index.set(beat.id, own !== void 0 ? { own, resolved } : { resolved });
88
+ };
89
+ const visit = (node, inherited) => {
90
+ const def = num(node.padAfterDefault) ?? inherited;
91
+ if (node.type === "group") {
92
+ if (node.prompt) set(node.prompt, def, false);
93
+ for (const child of node.children) visit(child, def);
94
+ return;
95
+ }
96
+ const beats = node.beats ?? [];
97
+ let last = -1;
98
+ beats.forEach((b, i) => {
99
+ if (spoken(b)) last = i;
100
+ });
101
+ beats.forEach((b, i) => {
102
+ if (spoken(b)) set(b, def, i === last);
103
+ });
104
+ };
105
+ const projectDefault = num(bundle.padAfterDefault) ?? import_model.DEFAULT_PAD_AFTER;
106
+ for (const scene of Object.values(bundle.scenes)) {
107
+ const sceneDefault = num(scene.padAfterDefault) ?? projectDefault;
108
+ for (const block of scene.blocks) {
109
+ const blockDefault = num(block.padAfterDefault) ?? sceneDefault;
110
+ for (const child of block.children) visit(child, blockDefault);
111
+ }
112
+ }
113
+ return index;
114
+ }
115
+
76
116
  // src/engine.ts
77
- var import_model2 = require("@patterkit/model");
117
+ var import_model3 = require("@patterkit/model");
78
118
  var astCache = /* @__PURE__ */ new WeakMap();
79
119
  var tagIndexCache = /* @__PURE__ */ new WeakMap();
80
120
  function tagIndexFor(bundle) {
@@ -85,6 +125,15 @@ function tagIndexFor(bundle) {
85
125
  }
86
126
  return index;
87
127
  }
128
+ var padIndexCache = /* @__PURE__ */ new WeakMap();
129
+ function padIndexFor(bundle) {
130
+ let index = padIndexCache.get(bundle);
131
+ if (!index) {
132
+ index = buildPadIndex(bundle);
133
+ padIndexCache.set(bundle, index);
134
+ }
135
+ return index;
136
+ }
88
137
  function tagsOf(host, id) {
89
138
  return host.tagIndex.get(id)?.slice();
90
139
  }
@@ -157,13 +206,13 @@ var Engine = class _Engine {
157
206
  const blockIndex = /* @__PURE__ */ new Map();
158
207
  const blockById = /* @__PURE__ */ new Map();
159
208
  for (const [sceneId, scene] of Object.entries(bundle.scenes)) {
160
- this.sceneGameIdToId.set((0, import_model.effectiveGameId)(scene), sceneId);
209
+ this.sceneGameIdToId.set((0, import_model2.effectiveGameId)(scene), sceneId);
161
210
  const blockAddrs = /* @__PURE__ */ new Map();
162
211
  for (const block of scene.blocks) {
163
212
  blockIndex.set(block.id, { sceneId });
164
213
  blockById.set(block.id, block);
165
- blockAddrs.set((0, import_model.effectiveGameId)(block), block.id);
166
- (0, import_model.walkNodes)(block.children, (n) => nodeIndex.set(n.id, n));
214
+ blockAddrs.set((0, import_model2.effectiveGameId)(block), block.id);
215
+ (0, import_model2.walkNodes)(block.children, (n) => nodeIndex.set(n.id, n));
167
216
  }
168
217
  this.blockGameIdToId.set(sceneId, blockAddrs);
169
218
  }
@@ -220,6 +269,7 @@ var Engine = class _Engine {
220
269
  blockGameIdToId: this.blockGameIdToId,
221
270
  // same instances the engine resolves with
222
271
  tagIndex: tagIndexFor(bundle),
272
+ padIndex: padIndexFor(bundle),
223
273
  registry,
224
274
  ownsRegistry,
225
275
  patterBag,
@@ -239,9 +289,9 @@ var Engine = class _Engine {
239
289
  replayPromptOnChoose: options.replayPromptOnChoose ?? false,
240
290
  captionsOn: options.closedCaptions ?? true,
241
291
  // captions shown by default (full text)
242
- captionOpen: (bundle.closedCaptions ?? import_model.DEFAULT_CAPTION_DELIMITERS).open,
243
- captionClose: (bundle.closedCaptions ?? import_model.DEFAULT_CAPTION_DELIMITERS).close,
244
- captionCharacter: bundle.closedCaptions?.character || import_model.DEFAULT_CAPTION_CHARACTER,
292
+ captionOpen: (bundle.closedCaptions ?? import_model2.DEFAULT_CAPTION_DELIMITERS).open,
293
+ captionClose: (bundle.closedCaptions ?? import_model2.DEFAULT_CAPTION_DELIMITERS).close,
294
+ captionCharacter: bundle.closedCaptions?.character || import_model2.DEFAULT_CAPTION_CHARACTER,
245
295
  // absent/empty -> SFX
246
296
  refSplitCache: /* @__PURE__ */ new Map(),
247
297
  refSplitRevision: registry.revision
@@ -466,11 +516,11 @@ var Engine = class _Engine {
466
516
  * The inverse of the resolve helpers - for a host that wants to display / log the address. */
467
517
  sceneAddress(sceneId) {
468
518
  const scene = this.host.bundle.scenes[sceneId];
469
- return scene ? (0, import_model.effectiveGameId)(scene) : void 0;
519
+ return scene ? (0, import_model2.effectiveGameId)(scene) : void 0;
470
520
  }
471
521
  blockAddress(blockId) {
472
522
  const block = this.host.blockById.get(blockId);
473
- return block ? (0, import_model.effectiveGameId)(block) : void 0;
523
+ return block ? (0, import_model2.effectiveGameId)(block) : void 0;
474
524
  }
475
525
  /**
476
526
  * Author tags (#215) accumulated for a beat by id: its own tags unioned with every ancestor's
@@ -553,13 +603,13 @@ var Engine = class _Engine {
553
603
  getOutline() {
554
604
  return Object.values(this.host.bundle.scenes).map((scene) => ({
555
605
  id: scene.id,
556
- ...(0, import_model.effectiveGameId)(scene) ? { gameId: (0, import_model.effectiveGameId)(scene) } : {},
606
+ ...(0, import_model2.effectiveGameId)(scene) ? { gameId: (0, import_model2.effectiveGameId)(scene) } : {},
557
607
  name: scene.name,
558
608
  ...gameDataField(scene.gameData),
559
609
  ...this.tagsField(scene.id),
560
610
  blocks: scene.blocks.map((block) => ({
561
611
  id: block.id,
562
- ...(0, import_model.effectiveGameId)(block) ? { gameId: (0, import_model.effectiveGameId)(block) } : {},
612
+ ...(0, import_model2.effectiveGameId)(block) ? { gameId: (0, import_model2.effectiveGameId)(block) } : {},
563
613
  name: block.name,
564
614
  ...gameDataField(block.gameData),
565
615
  ...this.tagsField(block.id),
@@ -576,7 +626,7 @@ var Engine = class _Engine {
576
626
  const out = [];
577
627
  for (const scene of Object.values(this.host.bundle.scenes)) {
578
628
  for (const block of scene.blocks) {
579
- (0, import_model.walkNodes)(block.children, (n) => {
629
+ (0, import_model2.walkNodes)(block.children, (n) => {
580
630
  if (n.type !== "snippet") return;
581
631
  for (const beat of n.beats ?? []) {
582
632
  out.push({ sceneId: scene.id, blockId: block.id, snippetId: n.id, beat: this.beatInfo(beat) });
@@ -613,17 +663,20 @@ var Engine = class _Engine {
613
663
  if (beat.kind === "line") {
614
664
  if (beat.character !== void 0) {
615
665
  info.character = beat.character;
616
- const name = this.host.defaultStrings[(0, import_model.castStringKey)(beat.character)] ?? this.host.castDisplay.get(beat.character);
666
+ const name = this.host.defaultStrings[(0, import_model2.castStringKey)(beat.character)] ?? this.host.castDisplay.get(beat.character);
617
667
  if (name !== void 0) info.characterName = name;
618
668
  }
619
669
  if (beat.direction !== void 0) info.direction = beat.direction;
620
670
  if (beat.qualifier !== void 0) {
621
671
  info.qualifier = beat.qualifier;
622
- const name = this.host.defaultStrings[(0, import_model.qualifierStringKey)(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
672
+ const name = this.host.defaultStrings[(0, import_model2.qualifierStringKey)(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
623
673
  if (name !== void 0) info.qualifierName = name;
624
674
  }
625
675
  }
626
676
  if (beat.kind === "line" || beat.kind === "text") {
677
+ const pad = this.host.padIndex.get(beat.id);
678
+ info.padAfter = pad?.resolved ?? import_model2.DEFAULT_PAD_AFTER;
679
+ if (pad?.own !== void 0) info.ownPadAfter = pad.own;
627
680
  const source = this.host.defaultStrings[beat.id];
628
681
  if (source !== void 0) info.text = source;
629
682
  }
@@ -849,7 +902,7 @@ var Engine = class _Engine {
849
902
  const flows = {};
850
903
  for (const [id, flow] of this.flowsById) flows[id] = flow.snapshot();
851
904
  return {
852
- version: import_model2.SAVE_VERSION,
905
+ version: import_model3.SAVE_VERSION,
853
906
  ...this.host.ownsRegistry ? { registry: this.host.registry.save() } : {},
854
907
  sharedVisits: Object.fromEntries(this.host.sharedVisits),
855
908
  sharedSelectors: serialiseSelectors(this.host.sharedSelectors),
@@ -866,7 +919,7 @@ var Engine = class _Engine {
866
919
  loadGame(save) {
867
920
  this.refuseInCheckpoint("loadGame");
868
921
  const version = save.version;
869
- if (version !== 2 && version !== import_model2.SAVE_VERSION) throw new Error(`unsupported save version: ${String(version)}`);
922
+ if (version !== 2 && version !== import_model3.SAVE_VERSION) throw new Error(`unsupported save version: ${String(version)}`);
870
923
  const flows = save.flows;
871
924
  if (typeof flows !== "object" || flows === null || Array.isArray(flows)) throw new Error("malformed save: no flows");
872
925
  this.assertExternalScopes();
@@ -1417,7 +1470,8 @@ var Flow = class {
1417
1470
  const node = this.host.nodeIndex.get(o.id);
1418
1471
  if (!node) continue;
1419
1472
  byId.set(o.id, node);
1420
- options.push({ ...o });
1473
+ const { prompt: saved, ...rest } = o;
1474
+ options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node) } } : {} });
1421
1475
  }
1422
1476
  if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };
1423
1477
  }
@@ -1426,7 +1480,7 @@ var Flow = class {
1426
1480
  if (this.pendingPromptOwnerId) {
1427
1481
  const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
1428
1482
  const beat = owner ? this.authoredPromptOf(owner) : void 0;
1429
- if (beat) this.pendingPrompt = { beat, ...c.pendingPrompt ? { shown: { ...c.pendingPrompt } } : {} };
1483
+ if (beat) this.pendingPrompt = { beat, ...c.pendingPrompt ? { shown: { ...c.pendingPrompt, padAfter: c.pendingPrompt.padAfter ?? this.padOf(beat.id) } } : {} };
1430
1484
  else this.pendingPromptOwnerId = null;
1431
1485
  }
1432
1486
  }
@@ -1796,7 +1850,7 @@ var Flow = class {
1796
1850
  case "gameEvent":
1797
1851
  return { type: "gameEvent", id: beat.id, gameData: beat.gameData, ...withTags };
1798
1852
  case "text":
1799
- return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), gameData: beat.gameData, ...withTags };
1853
+ return { type: "text", id: beat.id, text: this.interpolate(this.resolveString(beat.id)), padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
1800
1854
  case "line": {
1801
1855
  const raw = this.resolveString(beat.id);
1802
1856
  const off = !this.host.captionsOn;
@@ -1812,6 +1866,8 @@ var Flow = class {
1812
1866
  direction: silent ? void 0 : beat.direction,
1813
1867
  qualifier: silent ? void 0 : beat.qualifier,
1814
1868
  qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
1869
+ padAfter: this.padOf(beat.id),
1870
+ // a silent line still fires, so it still carries its pause
1815
1871
  gameData: beat.gameData,
1816
1872
  ...withTags
1817
1873
  };
@@ -1850,6 +1906,7 @@ var Flow = class {
1850
1906
  const beat = this.promptBeatOf(node);
1851
1907
  if (!beat) return void 0;
1852
1908
  const text = this.interpolate(this.resolveString(beat.id));
1909
+ const padAfter = this.padOf(beat.id);
1853
1910
  return beat.kind === "line" ? {
1854
1911
  kind: "line",
1855
1912
  text: this.captionLine(text),
@@ -1857,8 +1914,9 @@ var Flow = class {
1857
1914
  characterName: this.resolveCharacterName(beat.character),
1858
1915
  direction: beat.direction,
1859
1916
  qualifier: beat.qualifier,
1860
- qualifierName: this.resolveQualifierName(beat.qualifier)
1861
- } : { kind: "text", text };
1917
+ qualifierName: this.resolveQualifierName(beat.qualifier),
1918
+ padAfter
1919
+ } : { kind: "text", text, padAfter };
1862
1920
  }
1863
1921
  /** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
1864
1922
  authoredPromptOf(node) {
@@ -1869,7 +1927,7 @@ var Flow = class {
1869
1927
  promptResult(beat, shown) {
1870
1928
  const tags = tagsOf(this.host, beat.id);
1871
1929
  const withTags = tags && tags.length ? { tags } : {};
1872
- if (shown.kind === "text") return { type: "text", id: beat.id, text: shown.text, gameData: beat.gameData, ...withTags };
1930
+ if (shown.kind === "text") return { type: "text", id: beat.id, text: shown.text, padAfter: this.padOf(beat.id), gameData: beat.gameData, ...withTags };
1873
1931
  return {
1874
1932
  type: "line",
1875
1933
  id: beat.id,
@@ -1879,6 +1937,7 @@ var Flow = class {
1879
1937
  direction: shown.direction,
1880
1938
  qualifier: shown.qualifier,
1881
1939
  qualifierName: shown.qualifierName,
1940
+ padAfter: this.padOf(beat.id),
1882
1941
  gameData: beat.gameData,
1883
1942
  ...withTags
1884
1943
  };
@@ -1892,7 +1951,7 @@ var Flow = class {
1892
1951
  /** The first snippet with a line/text beat within a child list, depth-first in authored order. */
1893
1952
  firstTextSnippetIn(children) {
1894
1953
  let found;
1895
- (0, import_model.walkNodes)(children, (n) => {
1954
+ (0, import_model2.walkNodes)(children, (n) => {
1896
1955
  if (!found && n.type === "snippet" && (n.beats ?? []).some((b) => b.kind === "line" || b.kind === "text")) {
1897
1956
  found = n;
1898
1957
  }
@@ -1912,16 +1971,27 @@ var Flow = class {
1912
1971
  resolveCharacterName(character) {
1913
1972
  if (character === void 0) return void 0;
1914
1973
  if (this.host.emitIds) return void 0;
1915
- const key = (0, import_model.castStringKey)(character);
1974
+ const key = (0, import_model2.castStringKey)(character);
1916
1975
  return this.host.strings[key] ?? this.host.defaultStrings[key] ?? this.host.castDisplay.get(character);
1917
1976
  }
1977
+ /** The resolved pause of the beat an option shows as its prompt (its authored prompt, else its first content
1978
+ * line), for a saved option that predates prompts carrying one. */
1979
+ promptPadOf(node) {
1980
+ const beat = this.promptBeatOf(node);
1981
+ return beat ? this.padOf(beat.id) : import_model2.DEFAULT_PAD_AFTER;
1982
+ }
1983
+ /** A line or text beat's resolved pause (line padding): its own `padAfter`, else the nearest default above
1984
+ * it, else the project's, else the built-in one; clamped to zero on a snippet's last line. */
1985
+ padOf(beatId) {
1986
+ return this.host.padIndex.get(beatId)?.resolved ?? import_model2.DEFAULT_PAD_AFTER;
1987
+ }
1918
1988
  /** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
1919
1989
  * active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
1920
1990
  * maps the `qualifier` gameId itself). */
1921
1991
  resolveQualifierName(qualifier) {
1922
1992
  if (qualifier === void 0) return void 0;
1923
1993
  if (this.host.emitIds) return void 0;
1924
- const key = (0, import_model.qualifierStringKey)(qualifier);
1994
+ const key = (0, import_model2.qualifierStringKey)(qualifier);
1925
1995
  return this.host.strings[key] ?? this.host.defaultStrings[key] ?? this.host.qualifierDisplay.get(qualifier);
1926
1996
  }
1927
1997
  /** Split a ref into scope + name. Tokens: `@scene`, registered tokens, else `@patter` (incl. bare `@name`). */
@@ -2004,7 +2074,7 @@ var Flow = class {
2004
2074
  }
2005
2075
  };
2006
2076
  function collectCast(nodes, out) {
2007
- (0, import_model.walkNodes)(nodes, (n) => {
2077
+ (0, import_model2.walkNodes)(nodes, (n) => {
2008
2078
  if (n.type === "group") {
2009
2079
  if (n.prompt?.kind === "line" && n.prompt.character) out.add(n.prompt.character);
2010
2080
  return;
@@ -2022,6 +2092,7 @@ function savedPrompt(p) {
2022
2092
  if (p.direction !== void 0) out.direction = p.direction;
2023
2093
  if (p.qualifier !== void 0) out.qualifier = p.qualifier;
2024
2094
  if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
2095
+ if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
2025
2096
  return out;
2026
2097
  }
2027
2098
  function serialiseSelectors(map) {
@@ -2112,8 +2183,11 @@ function truthy(v) {
2112
2183
  return v.length > 0;
2113
2184
  }
2114
2185
 
2186
+ // src/index.ts
2187
+ var import_model5 = require("@patterkit/model");
2188
+
2115
2189
  // src/describe.ts
2116
- var import_model3 = require("@patterkit/model");
2190
+ var import_model4 = require("@patterkit/model");
2117
2191
  var isShared = (d, scopeDefault) => d.shared ?? scopeDefault;
2118
2192
  function summariseProperty(d, scopeDefault) {
2119
2193
  return {
@@ -2166,11 +2240,11 @@ function describeBundle(bundle) {
2166
2240
  const sceneProps = [];
2167
2241
  for (const scene of Object.values(bundle.scenes)) {
2168
2242
  counts.scenes++;
2169
- const gameId = (0, import_model3.effectiveGameId)(scene);
2243
+ const gameId = (0, import_model4.effectiveGameId)(scene);
2170
2244
  addresses.push({
2171
2245
  gameId,
2172
2246
  name: scene.name,
2173
- blocks: scene.blocks.map((b) => ({ gameId: (0, import_model3.effectiveGameId)(b), name: b.name }))
2247
+ blocks: scene.blocks.map((b) => ({ gameId: (0, import_model4.effectiveGameId)(b), name: b.name }))
2174
2248
  });
2175
2249
  for (const block of scene.blocks) countBlock(block, counts);
2176
2250
  if (scene.sceneProps?.length) {
@@ -2235,6 +2309,7 @@ function effectiveGameData(fields, node) {
2235
2309
  // Annotate the CommonJS export names for ESM import in node:
2236
2310
  0 && (module.exports = {
2237
2311
  Checkpoint,
2312
+ DEFAULT_PAD_AFTER,
2238
2313
  Engine,
2239
2314
  Flow,
2240
2315
  buildTagIndex,