@patterkit/runtime 0.21.0 → 0.22.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,17 @@ version number always means the same runtime behaviour. This package is versione
33
33
 
34
34
  ## [Unreleased]
35
35
 
36
+ ## [0.22.0] - 2026-10-10
37
+
38
+ ### Added
39
+
40
+ - **Speaker qualifiers**: a line can say how it is delivered, a screenplay's `TAM (O.S.)`, without becoming a
41
+ second character. A `line` step, and a line-kind choice prompt, carries `qualifier`, the qualifier's `gameId`
42
+ (`vo`, `os`, `radio`, or the project's own), and `qualifierName`, its shown name, resolved as `characterName`
43
+ is: the active locale's `qualifier:<gameId>` string, then the default locale's, then the authored name.
44
+ `qualifierName` is absent in an IDs-only build, and both are dropped on a line silenced by closed captions.
45
+ Saves and the outline and beat sequence carry them too. What a qualifier means is the game's to decide.
46
+
36
47
  ## [0.21.0] - 2026-10-07
37
48
 
38
49
  ### Changed
package/dist/index.cjs CHANGED
@@ -150,6 +150,8 @@ var Engine = class _Engine {
150
150
  }
151
151
  const castDisplay = /* @__PURE__ */ new Map();
152
152
  for (const c of bundle.cast ?? []) if (c.displayName) castDisplay.set(c.name, c.displayName);
153
+ const qualifierDisplay = /* @__PURE__ */ new Map();
154
+ for (const q of bundle.qualifiers ?? []) if (q?.gameId) qualifierDisplay.set(q.gameId, q.name);
153
155
  this.defaultSeed = (options.seed ?? 2654435769) >>> 0;
154
156
  const nodeIndex = /* @__PURE__ */ new Map();
155
157
  const blockIndex = /* @__PURE__ */ new Map();
@@ -210,6 +212,7 @@ var Engine = class _Engine {
210
212
  strings,
211
213
  defaultStrings,
212
214
  castDisplay,
215
+ qualifierDisplay,
213
216
  nodeIndex,
214
217
  blockIndex,
215
218
  blockById,
@@ -614,6 +617,11 @@ var Engine = class _Engine {
614
617
  if (name !== void 0) info.characterName = name;
615
618
  }
616
619
  if (beat.direction !== void 0) info.direction = beat.direction;
620
+ if (beat.qualifier !== void 0) {
621
+ info.qualifier = beat.qualifier;
622
+ const name = this.host.defaultStrings[(0, import_model.qualifierStringKey)(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
623
+ if (name !== void 0) info.qualifierName = name;
624
+ }
617
625
  }
618
626
  if (beat.kind === "line" || beat.kind === "text") {
619
627
  const source = this.host.defaultStrings[beat.id];
@@ -1802,6 +1810,8 @@ var Flow = class {
1802
1810
  character: silent ? void 0 : beat.character,
1803
1811
  characterName: silent ? void 0 : this.resolveCharacterName(beat.character),
1804
1812
  direction: silent ? void 0 : beat.direction,
1813
+ qualifier: silent ? void 0 : beat.qualifier,
1814
+ qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
1805
1815
  gameData: beat.gameData,
1806
1816
  ...withTags
1807
1817
  };
@@ -1840,7 +1850,15 @@ var Flow = class {
1840
1850
  const beat = this.promptBeatOf(node);
1841
1851
  if (!beat) return void 0;
1842
1852
  const text = this.interpolate(this.resolveString(beat.id));
1843
- return beat.kind === "line" ? { kind: "line", text: this.captionLine(text), character: beat.character, characterName: this.resolveCharacterName(beat.character), direction: beat.direction } : { kind: "text", text };
1853
+ return beat.kind === "line" ? {
1854
+ kind: "line",
1855
+ text: this.captionLine(text),
1856
+ character: beat.character,
1857
+ characterName: this.resolveCharacterName(beat.character),
1858
+ direction: beat.direction,
1859
+ qualifier: beat.qualifier,
1860
+ qualifierName: this.resolveQualifierName(beat.qualifier)
1861
+ } : { kind: "text", text };
1844
1862
  }
1845
1863
  /** An option's AUTHORED prompt beat: an Option group's own `prompt`. The only prompt a replay speaks. */
1846
1864
  authoredPromptOf(node) {
@@ -1859,6 +1877,8 @@ var Flow = class {
1859
1877
  character: shown.character,
1860
1878
  characterName: shown.characterName,
1861
1879
  direction: shown.direction,
1880
+ qualifier: shown.qualifier,
1881
+ qualifierName: shown.qualifierName,
1862
1882
  gameData: beat.gameData,
1863
1883
  ...withTags
1864
1884
  };
@@ -1895,6 +1915,15 @@ var Flow = class {
1895
1915
  const key = (0, import_model.castStringKey)(character);
1896
1916
  return this.host.strings[key] ?? this.host.defaultStrings[key] ?? this.host.castDisplay.get(character);
1897
1917
  }
1918
+ /** A speaker qualifier's shown name, resolved as a character's is: the `qualifier:<gameId>` string in the
1919
+ * active locale, else the default locale, else the authored name. Undefined in IDs-only mode (the game
1920
+ * maps the `qualifier` gameId itself). */
1921
+ resolveQualifierName(qualifier) {
1922
+ if (qualifier === void 0) return void 0;
1923
+ if (this.host.emitIds) return void 0;
1924
+ const key = (0, import_model.qualifierStringKey)(qualifier);
1925
+ return this.host.strings[key] ?? this.host.defaultStrings[key] ?? this.host.qualifierDisplay.get(qualifier);
1926
+ }
1898
1927
  /** Split a ref into scope + name. Tokens: `@scene`, registered tokens, else `@patter` (incl. bare `@name`). */
1899
1928
  splitRef(ref) {
1900
1929
  return splitHostRef(this.host, ref);
@@ -1991,6 +2020,8 @@ function savedPrompt(p) {
1991
2020
  if (p.character !== void 0) out.character = p.character;
1992
2021
  if (p.characterName !== void 0) out.characterName = p.characterName;
1993
2022
  if (p.direction !== void 0) out.direction = p.direction;
2023
+ if (p.qualifier !== void 0) out.qualifier = p.qualifier;
2024
+ if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
1994
2025
  return out;
1995
2026
  }
1996
2027
  function serialiseSelectors(map) {