@patterkit/runtime 0.24.0 → 0.25.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
@@ -22,6 +22,7 @@ type StepResult = {
22
22
  direction?: string;
23
23
  qualifier?: string;
24
24
  qualifierName?: string;
25
+ speakerLabel?: string;
25
26
  padAfter: number;
26
27
  gameData?: GameData;
27
28
  tags?: string[];
@@ -58,6 +59,8 @@ interface BeatInfo {
58
59
  qualifier?: string;
59
60
  /** The qualifier's shown name (source locale), e.g. `V.O.`. */
60
61
  qualifierName?: string;
62
+ /** The speaker's label (source locale): `You (V.O.)`, a pair's own label, or with no qualifier the name. */
63
+ speakerLabel?: string;
61
64
  /** The pause after this beat, in seconds, resolved from its own value and the defaults above it (line and
62
65
  * text only; see line padding). What a delivered step carries. */
63
66
  padAfter?: number;
@@ -133,6 +136,8 @@ interface ChoicePrompt {
133
136
  /** The speaker qualifier's `gameId` (`vo`), and its resolved shown name (locale-aware). */
134
137
  qualifier?: string;
135
138
  qualifierName?: string;
139
+ /** The name to show for the speaker (locale-aware): see {@link speakerLabelOf}. */
140
+ speakerLabel?: string;
136
141
  /** The prompt's resolved pause, in seconds: when it is spoken (by `replayPromptOnChoose`, or by a game that
137
142
  * voices it), the wait before the reply, negative to cut in on the question (line padding). */
138
143
  padAfter: number;
@@ -379,6 +384,11 @@ interface FlowHost {
379
384
  castDisplay: Map<string, string>;
380
385
  /** Speaker qualifier gameId -> authored name (the unlocalised fallback when no loc string exists). */
381
386
  qualifierDisplay: Map<string, string>;
387
+ /** `<name>\0<gameId>` -> a character's own label with that qualifier, for the overridden pairs only (the
388
+ * unlocalised fallback). A pair not here follows the pattern, whatever a loc file holds for it. */
389
+ speakerLabels: Map<string, string>;
390
+ /** The project's speaker pattern (the unlocalised fallback for the `speaker:pattern` string). */
391
+ speakerPattern: string;
382
392
  nodeIndex: Map<string, SelectableNode>;
383
393
  blockIndex: Map<string, {
384
394
  sceneId: string;
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ type StepResult = {
22
22
  direction?: string;
23
23
  qualifier?: string;
24
24
  qualifierName?: string;
25
+ speakerLabel?: string;
25
26
  padAfter: number;
26
27
  gameData?: GameData;
27
28
  tags?: string[];
@@ -58,6 +59,8 @@ interface BeatInfo {
58
59
  qualifier?: string;
59
60
  /** The qualifier's shown name (source locale), e.g. `V.O.`. */
60
61
  qualifierName?: string;
62
+ /** The speaker's label (source locale): `You (V.O.)`, a pair's own label, or with no qualifier the name. */
63
+ speakerLabel?: string;
61
64
  /** The pause after this beat, in seconds, resolved from its own value and the defaults above it (line and
62
65
  * text only; see line padding). What a delivered step carries. */
63
66
  padAfter?: number;
@@ -133,6 +136,8 @@ interface ChoicePrompt {
133
136
  /** The speaker qualifier's `gameId` (`vo`), and its resolved shown name (locale-aware). */
134
137
  qualifier?: string;
135
138
  qualifierName?: string;
139
+ /** The name to show for the speaker (locale-aware): see {@link speakerLabelOf}. */
140
+ speakerLabel?: string;
136
141
  /** The prompt's resolved pause, in seconds: when it is spoken (by `replayPromptOnChoose`, or by a game that
137
142
  * voices it), the wait before the reply, negative to cut in on the question (line padding). */
138
143
  padAfter: number;
@@ -379,6 +384,11 @@ interface FlowHost {
379
384
  castDisplay: Map<string, string>;
380
385
  /** Speaker qualifier gameId -> authored name (the unlocalised fallback when no loc string exists). */
381
386
  qualifierDisplay: Map<string, string>;
387
+ /** `<name>\0<gameId>` -> a character's own label with that qualifier, for the overridden pairs only (the
388
+ * unlocalised fallback). A pair not here follows the pattern, whatever a loc file holds for it. */
389
+ speakerLabels: Map<string, string>;
390
+ /** The project's speaker pattern (the unlocalised fallback for the `speaker:pattern` string). */
391
+ speakerPattern: string;
382
392
  nodeIndex: Map<string, SelectableNode>;
383
393
  blockIndex: Map<string, {
384
394
  sceneId: string;
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_PAD_AFTER as DEFAULT_PAD_AFTER2, DEFAULT_CAPTION_DELIMITERS, DEFAULT_CAPTION_CHARACTER } from "@patterkit/model";
7
+ import { walkNodes, effectiveGameId, castStringKey, qualifierStringKey, speakerLabelKey, formatSpeakerLabel, SPEAKER_PATTERN_KEY, DEFAULT_SPEAKER_PATTERN, 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) {
@@ -168,6 +168,9 @@ var Engine = class _Engine {
168
168
  for (const c of bundle.cast ?? []) if (c.displayName) castDisplay.set(c.name, c.displayName);
169
169
  const qualifierDisplay = /* @__PURE__ */ new Map();
170
170
  for (const q of bundle.qualifiers ?? []) if (q?.gameId) qualifierDisplay.set(q.gameId, q.name);
171
+ const speakerLabels = /* @__PURE__ */ new Map();
172
+ for (const c of bundle.cast ?? []) for (const [q, label] of Object.entries(c.qualifierLabels ?? {})) if (typeof label === "string") speakerLabels.set(`${c.name}\0${q}`, label);
173
+ const speakerPattern = bundle.speakerPattern ?? DEFAULT_SPEAKER_PATTERN;
171
174
  this.defaultSeed = (options.seed ?? 2654435769) >>> 0;
172
175
  const nodeIndex = /* @__PURE__ */ new Map();
173
176
  const blockIndex = /* @__PURE__ */ new Map();
@@ -229,6 +232,8 @@ var Engine = class _Engine {
229
232
  defaultStrings,
230
233
  castDisplay,
231
234
  qualifierDisplay,
235
+ speakerLabels,
236
+ speakerPattern,
232
237
  nodeIndex,
233
238
  blockIndex,
234
239
  blockById,
@@ -639,6 +644,8 @@ var Engine = class _Engine {
639
644
  const name = this.host.defaultStrings[qualifierStringKey(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
640
645
  if (name !== void 0) info.qualifierName = name;
641
646
  }
647
+ const label = speakerLabelOf(this.host, beat.character, beat.qualifier, true);
648
+ if (label !== void 0) info.speakerLabel = label;
642
649
  }
643
650
  if (beat.kind === "line" || beat.kind === "text") {
644
651
  const pad = this.host.padIndex.get(beat.id);
@@ -1477,7 +1484,8 @@ var Flow = class {
1477
1484
  if (!node) continue;
1478
1485
  byId.set(o.id, node);
1479
1486
  const { prompt: saved, ...rest } = o;
1480
- options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node) } } : {} });
1487
+ const label = saved?.kind === "line" && saved.speakerLabel === void 0 ? speakerLabelOf(this.host, saved.character, saved.qualifier) : void 0;
1488
+ options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node), ...label !== void 0 ? { speakerLabel: label } : {} } } : {} });
1481
1489
  }
1482
1490
  if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };
1483
1491
  }
@@ -1486,7 +1494,9 @@ var Flow = class {
1486
1494
  if (this.pendingPromptOwnerId) {
1487
1495
  const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
1488
1496
  const beat = owner ? this.authoredPromptOf(owner) : void 0;
1489
- if (beat) this.pendingPrompt = { beat, ...c.pendingPrompt ? { shown: { ...c.pendingPrompt, padAfter: c.pendingPrompt.padAfter ?? this.padOf(beat.id) } } : {} };
1497
+ const saved = c.pendingPrompt;
1498
+ const label = saved?.kind === "line" && saved.speakerLabel === void 0 ? speakerLabelOf(this.host, saved.character, saved.qualifier) : void 0;
1499
+ if (beat) this.pendingPrompt = { beat, ...saved ? { shown: { ...saved, padAfter: saved.padAfter ?? this.padOf(beat.id), ...label !== void 0 ? { speakerLabel: label } : {} } } : {} };
1490
1500
  else this.pendingPromptOwnerId = null;
1491
1501
  }
1492
1502
  }
@@ -1872,6 +1882,7 @@ var Flow = class {
1872
1882
  direction: silent ? void 0 : beat.direction,
1873
1883
  qualifier: silent ? void 0 : beat.qualifier,
1874
1884
  qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
1885
+ speakerLabel: silent ? void 0 : speakerLabelOf(this.host, beat.character, beat.qualifier),
1875
1886
  padAfter: this.padOf(beat.id),
1876
1887
  // a silent line still fires, so it still carries its pause
1877
1888
  gameData: beat.gameData,
@@ -1921,6 +1932,7 @@ var Flow = class {
1921
1932
  direction: beat.direction,
1922
1933
  qualifier: beat.qualifier,
1923
1934
  qualifierName: this.resolveQualifierName(beat.qualifier),
1935
+ speakerLabel: speakerLabelOf(this.host, beat.character, beat.qualifier),
1924
1936
  padAfter
1925
1937
  } : { kind: "text", text, padAfter };
1926
1938
  }
@@ -1943,6 +1955,7 @@ var Flow = class {
1943
1955
  direction: shown.direction,
1944
1956
  qualifier: shown.qualifier,
1945
1957
  qualifierName: shown.qualifierName,
1958
+ speakerLabel: shown.speakerLabel,
1946
1959
  padAfter: this.padOf(beat.id),
1947
1960
  gameData: beat.gameData,
1948
1961
  ...withTags
@@ -2088,6 +2101,16 @@ function collectCast(nodes, out) {
2088
2101
  for (const beat of n.beats ?? []) if (beat.kind === "line" && beat.character) out.add(beat.character);
2089
2102
  });
2090
2103
  }
2104
+ function speakerLabelOf(host, character, qualifier, source = false) {
2105
+ if (!character || host.emitIds) return void 0;
2106
+ const lookup = (key) => (source ? void 0 : host.strings[key]) ?? host.defaultStrings[key];
2107
+ const name = lookup(castStringKey(character)) ?? host.castDisplay.get(character) ?? character;
2108
+ if (qualifier === void 0) return name;
2109
+ const own = host.speakerLabels.get(`${character}\0${qualifier}`);
2110
+ if (own !== void 0) return lookup(speakerLabelKey(character, qualifier)) ?? own;
2111
+ const shown = lookup(qualifierStringKey(qualifier)) ?? host.qualifierDisplay.get(qualifier) ?? qualifier;
2112
+ return formatSpeakerLabel(lookup(SPEAKER_PATTERN_KEY) ?? host.speakerPattern, name, shown);
2113
+ }
2091
2114
  function gameDataField(gameData) {
2092
2115
  return gameData && Object.keys(gameData).length ? { gameData } : {};
2093
2116
  }
@@ -2098,6 +2121,7 @@ function savedPrompt(p) {
2098
2121
  if (p.direction !== void 0) out.direction = p.direction;
2099
2122
  if (p.qualifier !== void 0) out.qualifier = p.qualifier;
2100
2123
  if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
2124
+ if (p.speakerLabel !== void 0) out.speakerLabel = p.speakerLabel;
2101
2125
  if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
2102
2126
  return out;
2103
2127
  }