@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/CHANGELOG.md CHANGED
@@ -33,6 +33,19 @@ version number always means the same runtime behaviour. This package is versione
33
33
 
34
34
  ## [Unreleased]
35
35
 
36
+ ## [0.25.0] - 2026-10-14
37
+
38
+ ### Added
39
+
40
+ - **Speaker labels**: a line step, a line choice prompt, and a beat's info carry `speakerLabel`, the name to
41
+ show for the speaker, localised. With a qualifier it is the character's own label for that pair when the
42
+ project gives one (`speaker:<NAME>:<qualifier>`), else the project's speaker pattern (`speaker:pattern`,
43
+ `{name} ({qualifier})` by default) filled with the shown name and the qualifier's shown name; with none, the
44
+ shown name (the token when the cast gives none). Each piece follows the active locale, then the default, then
45
+ the authored value. A translated pair label counts only for a pair the project overrides. Absent with no
46
+ speaker, on a silenced line, and in IDs-only mode. Saved choice prompts carry it, and an older save works it
47
+ out again on load.
48
+
36
49
  ## [0.24.0] - 2026-10-11
37
50
 
38
51
  ### Added
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ian Thomas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs CHANGED
@@ -202,6 +202,9 @@ var Engine = class _Engine {
202
202
  for (const c of bundle.cast ?? []) if (c.displayName) castDisplay.set(c.name, c.displayName);
203
203
  const qualifierDisplay = /* @__PURE__ */ new Map();
204
204
  for (const q of bundle.qualifiers ?? []) if (q?.gameId) qualifierDisplay.set(q.gameId, q.name);
205
+ const speakerLabels = /* @__PURE__ */ new Map();
206
+ 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);
207
+ const speakerPattern = bundle.speakerPattern ?? import_model2.DEFAULT_SPEAKER_PATTERN;
205
208
  this.defaultSeed = (options.seed ?? 2654435769) >>> 0;
206
209
  const nodeIndex = /* @__PURE__ */ new Map();
207
210
  const blockIndex = /* @__PURE__ */ new Map();
@@ -263,6 +266,8 @@ var Engine = class _Engine {
263
266
  defaultStrings,
264
267
  castDisplay,
265
268
  qualifierDisplay,
269
+ speakerLabels,
270
+ speakerPattern,
266
271
  nodeIndex,
267
272
  blockIndex,
268
273
  blockById,
@@ -673,6 +678,8 @@ var Engine = class _Engine {
673
678
  const name = this.host.defaultStrings[(0, import_model2.qualifierStringKey)(beat.qualifier)] ?? this.host.qualifierDisplay.get(beat.qualifier);
674
679
  if (name !== void 0) info.qualifierName = name;
675
680
  }
681
+ const label = speakerLabelOf(this.host, beat.character, beat.qualifier, true);
682
+ if (label !== void 0) info.speakerLabel = label;
676
683
  }
677
684
  if (beat.kind === "line" || beat.kind === "text") {
678
685
  const pad = this.host.padIndex.get(beat.id);
@@ -1511,7 +1518,8 @@ var Flow = class {
1511
1518
  if (!node) continue;
1512
1519
  byId.set(o.id, node);
1513
1520
  const { prompt: saved, ...rest } = o;
1514
- options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node) } } : {} });
1521
+ const label = saved?.kind === "line" && saved.speakerLabel === void 0 ? speakerLabelOf(this.host, saved.character, saved.qualifier) : void 0;
1522
+ options.push({ ...rest, ...saved ? { prompt: { ...saved, padAfter: saved.padAfter ?? this.promptPadOf(node), ...label !== void 0 ? { speakerLabel: label } : {} } } : {} });
1515
1523
  }
1516
1524
  if (options.length > 0) this.pendingChoice = { groupId: c.pendingChoice.groupId, options, byId };
1517
1525
  }
@@ -1520,7 +1528,9 @@ var Flow = class {
1520
1528
  if (this.pendingPromptOwnerId) {
1521
1529
  const owner = this.host.nodeIndex.get(this.pendingPromptOwnerId);
1522
1530
  const beat = owner ? this.authoredPromptOf(owner) : void 0;
1523
- if (beat) this.pendingPrompt = { beat, ...c.pendingPrompt ? { shown: { ...c.pendingPrompt, padAfter: c.pendingPrompt.padAfter ?? this.padOf(beat.id) } } : {} };
1531
+ const saved = c.pendingPrompt;
1532
+ const label = saved?.kind === "line" && saved.speakerLabel === void 0 ? speakerLabelOf(this.host, saved.character, saved.qualifier) : void 0;
1533
+ if (beat) this.pendingPrompt = { beat, ...saved ? { shown: { ...saved, padAfter: saved.padAfter ?? this.padOf(beat.id), ...label !== void 0 ? { speakerLabel: label } : {} } } : {} };
1524
1534
  else this.pendingPromptOwnerId = null;
1525
1535
  }
1526
1536
  }
@@ -1906,6 +1916,7 @@ var Flow = class {
1906
1916
  direction: silent ? void 0 : beat.direction,
1907
1917
  qualifier: silent ? void 0 : beat.qualifier,
1908
1918
  qualifierName: silent ? void 0 : this.resolveQualifierName(beat.qualifier),
1919
+ speakerLabel: silent ? void 0 : speakerLabelOf(this.host, beat.character, beat.qualifier),
1909
1920
  padAfter: this.padOf(beat.id),
1910
1921
  // a silent line still fires, so it still carries its pause
1911
1922
  gameData: beat.gameData,
@@ -1955,6 +1966,7 @@ var Flow = class {
1955
1966
  direction: beat.direction,
1956
1967
  qualifier: beat.qualifier,
1957
1968
  qualifierName: this.resolveQualifierName(beat.qualifier),
1969
+ speakerLabel: speakerLabelOf(this.host, beat.character, beat.qualifier),
1958
1970
  padAfter
1959
1971
  } : { kind: "text", text, padAfter };
1960
1972
  }
@@ -1977,6 +1989,7 @@ var Flow = class {
1977
1989
  direction: shown.direction,
1978
1990
  qualifier: shown.qualifier,
1979
1991
  qualifierName: shown.qualifierName,
1992
+ speakerLabel: shown.speakerLabel,
1980
1993
  padAfter: this.padOf(beat.id),
1981
1994
  gameData: beat.gameData,
1982
1995
  ...withTags
@@ -2122,6 +2135,16 @@ function collectCast(nodes, out) {
2122
2135
  for (const beat of n.beats ?? []) if (beat.kind === "line" && beat.character) out.add(beat.character);
2123
2136
  });
2124
2137
  }
2138
+ function speakerLabelOf(host, character, qualifier, source = false) {
2139
+ if (!character || host.emitIds) return void 0;
2140
+ const lookup = (key) => (source ? void 0 : host.strings[key]) ?? host.defaultStrings[key];
2141
+ const name = lookup((0, import_model2.castStringKey)(character)) ?? host.castDisplay.get(character) ?? character;
2142
+ if (qualifier === void 0) return name;
2143
+ const own = host.speakerLabels.get(`${character}\0${qualifier}`);
2144
+ if (own !== void 0) return lookup((0, import_model2.speakerLabelKey)(character, qualifier)) ?? own;
2145
+ const shown = lookup((0, import_model2.qualifierStringKey)(qualifier)) ?? host.qualifierDisplay.get(qualifier) ?? qualifier;
2146
+ return (0, import_model2.formatSpeakerLabel)(lookup(import_model2.SPEAKER_PATTERN_KEY) ?? host.speakerPattern, name, shown);
2147
+ }
2125
2148
  function gameDataField(gameData) {
2126
2149
  return gameData && Object.keys(gameData).length ? { gameData } : {};
2127
2150
  }
@@ -2132,6 +2155,7 @@ function savedPrompt(p) {
2132
2155
  if (p.direction !== void 0) out.direction = p.direction;
2133
2156
  if (p.qualifier !== void 0) out.qualifier = p.qualifier;
2134
2157
  if (p.qualifierName !== void 0) out.qualifierName = p.qualifierName;
2158
+ if (p.speakerLabel !== void 0) out.speakerLabel = p.speakerLabel;
2135
2159
  if (typeof p.padAfter === "number") out.padAfter = p.padAfter;
2136
2160
  return out;
2137
2161
  }