@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 +14 -0
- package/README.md +9 -2
- package/dist/index.cjs +105 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +81 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
166
|
-
(0,
|
|
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 ??
|
|
243
|
-
captionClose: (bundle.closedCaptions ??
|
|
244
|
-
captionCharacter: bundle.closedCaptions?.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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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:
|
|
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 !==
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|