@patterkit/model 0.13.1 → 0.14.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.cjs CHANGED
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  DEFAULT_PAD_AFTER: () => DEFAULT_PAD_AFTER,
30
30
  DEFAULT_QUALIFIERS: () => DEFAULT_QUALIFIERS,
31
31
  DEFAULT_RECORDING_STATUSES: () => DEFAULT_RECORDING_STATUSES,
32
+ DEFAULT_SPEAKER_PATTERN: () => DEFAULT_SPEAKER_PATTERN,
32
33
  DEFAULT_WRITING_STATUSES: () => DEFAULT_WRITING_STATUSES,
33
34
  FLOW_SCHEMA: () => FLOW_SCHEMA,
34
35
  HANDOFF_SCHEMA: () => HANDOFF_SCHEMA,
@@ -41,10 +42,12 @@ __export(index_exports, {
41
42
  RESERVED_PROPERTY_NAMES: () => import_expr.RESERVED_PROPERTY_NAMES,
42
43
  SAVE_SCHEMA: () => SAVE_SCHEMA,
43
44
  SAVE_VERSION: () => SAVE_VERSION,
45
+ SPEAKER_PATTERN_KEY: () => SPEAKER_PATTERN_KEY,
44
46
  STRINGS_SCHEMA: () => STRINGS_SCHEMA,
45
47
  castStringKey: () => castStringKey,
46
48
  deriveRecordingFolders: () => deriveRecordingFolders,
47
49
  effectiveGameId: () => effectiveGameId,
50
+ formatSpeakerLabel: () => formatSpeakerLabel,
48
51
  gameIdify: () => gameIdify,
49
52
  isCaseOnlyPropertyName: () => import_expr.isCaseOnlyPropertyName,
50
53
  isContentlessBeat: () => isContentlessBeat,
@@ -54,7 +57,9 @@ __export(index_exports, {
54
57
  projectQualifiers: () => projectQualifiers,
55
58
  propertyNameify: () => import_expr.propertyNameify,
56
59
  qualifierStringKey: () => qualifierStringKey,
60
+ speakerLabelKey: () => speakerLabelKey,
57
61
  usedQualifiers: () => usedQualifiers,
62
+ usedSpeakerPairs: () => usedSpeakerPairs,
58
63
  walkNodes: () => walkNodes
59
64
  });
60
65
  module.exports = __toCommonJS(index_exports);
@@ -107,6 +112,29 @@ function usedQualifiers(project, scenes) {
107
112
  }
108
113
  return projectQualifiers(project).filter((q) => used.has(q.gameId));
109
114
  }
115
+ function usedSpeakerPairs(scenes) {
116
+ const seen = /* @__PURE__ */ new Set();
117
+ const out = [];
118
+ const add = (beat) => {
119
+ if (!beat.character || !beat.qualifier) return;
120
+ const key = `${beat.character}\0${beat.qualifier}`;
121
+ if (seen.has(key)) return;
122
+ seen.add(key);
123
+ out.push({ character: beat.character, qualifier: beat.qualifier });
124
+ };
125
+ for (const scene of scenes) {
126
+ for (const block of scene.blocks) {
127
+ walkNodes(block.children, (node) => {
128
+ if (node.type === "group") {
129
+ if (node.prompt?.kind === "line") add(node.prompt);
130
+ return;
131
+ }
132
+ for (const beat of node.beats ?? []) if (beat.kind === "line") add(beat);
133
+ });
134
+ }
135
+ }
136
+ return out;
137
+ }
110
138
  var DEFAULT_PAD_AFTER = 0.6;
111
139
  var PAD_AFTER_MIN = -10;
112
140
  var PAD_AFTER_MAX = 60;
@@ -158,6 +186,14 @@ function castStringKey(name) {
158
186
  function qualifierStringKey(gameId) {
159
187
  return `qualifier:${gameId}`;
160
188
  }
189
+ function speakerLabelKey(name, gameId) {
190
+ return `speaker:${name}:${gameId}`;
191
+ }
192
+ var SPEAKER_PATTERN_KEY = "speaker:pattern";
193
+ var DEFAULT_SPEAKER_PATTERN = "{name} ({qualifier})";
194
+ function formatSpeakerLabel(pattern, name, qualifier) {
195
+ return pattern.split("{name}").map((part) => part.split("{qualifier}").join(qualifier)).join(name);
196
+ }
161
197
  var HANDOFF_SCHEMA = "patter/handoff@0";
162
198
  var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
163
199
  var DEFAULT_CAPTION_CHARACTER = "SFX";
@@ -182,6 +218,7 @@ var AUTHORING_SCHEMA = "patter/authoring@0";
182
218
  DEFAULT_PAD_AFTER,
183
219
  DEFAULT_QUALIFIERS,
184
220
  DEFAULT_RECORDING_STATUSES,
221
+ DEFAULT_SPEAKER_PATTERN,
185
222
  DEFAULT_WRITING_STATUSES,
186
223
  FLOW_SCHEMA,
187
224
  HANDOFF_SCHEMA,
@@ -194,10 +231,12 @@ var AUTHORING_SCHEMA = "patter/authoring@0";
194
231
  RESERVED_PROPERTY_NAMES,
195
232
  SAVE_SCHEMA,
196
233
  SAVE_VERSION,
234
+ SPEAKER_PATTERN_KEY,
197
235
  STRINGS_SCHEMA,
198
236
  castStringKey,
199
237
  deriveRecordingFolders,
200
238
  effectiveGameId,
239
+ formatSpeakerLabel,
201
240
  gameIdify,
202
241
  isCaseOnlyPropertyName,
203
242
  isContentlessBeat,
@@ -207,6 +246,8 @@ var AUTHORING_SCHEMA = "patter/authoring@0";
207
246
  projectQualifiers,
208
247
  propertyNameify,
209
248
  qualifierStringKey,
249
+ speakerLabelKey,
210
250
  usedQualifiers,
251
+ usedSpeakerPairs,
211
252
  walkNodes
212
253
  });
package/dist/index.d.cts CHANGED
@@ -352,6 +352,10 @@ interface CastMember {
352
352
  /** The voice actor cast for this character, if known - surfaced in the VO script export (spec §16).
353
353
  * Authoring-only (not shipped in the runtime bundle). */
354
354
  actor?: string;
355
+ /** This character's own label with a qualifier, by the qualifier's `gameId`, in the source language
356
+ * (`{ vo: "Your thoughts" }`), where the project's speaker pattern shouldn't apply. Localised as the
357
+ * `speaker:<name>:<gameId>` string (`speakerLabelKey`). Absent for every pair that follows the pattern. */
358
+ qualifierLabels?: Record<string, string>;
355
359
  gameData?: GameData;
356
360
  }
357
361
  /** A speaker qualifier (a screenplay's character extension): `V.O.`, `O.S.`, `RADIO`, or the project's own.
@@ -372,6 +376,12 @@ declare const DEFAULT_QUALIFIERS: SpeakerQualifier[];
372
376
  declare function projectQualifiers(project: Pick<ProjectFile, "qualifiers">): SpeakerQualifier[];
373
377
  /** The project's qualifiers that some line (or line prompt) in these scenes uses, in the project's order. */
374
378
  declare function usedQualifiers(project: Pick<ProjectFile, "qualifiers">, scenes: Scene[]): SpeakerQualifier[];
379
+ /** Every character and qualifier pair the lines use (a line's speaker with its qualifier, option prompts
380
+ * included), once each, in the order first met. A pair's label is shown only for these. */
381
+ declare function usedSpeakerPairs(scenes: Scene[]): {
382
+ character: string;
383
+ qualifier: string;
384
+ }[];
375
385
  /** The built-in `padAfter`, in seconds, for a project that sets no default of its own: the same on every runtime
376
386
  * and in Patterpad's Play window (design/proposals/line-padding.md section 4). */
377
387
  declare const DEFAULT_PAD_AFTER = 0.6;
@@ -623,6 +633,9 @@ interface ProjectFile {
623
633
  /** The speaker qualifiers a line can pick from, in display order; default `DEFAULT_QUALIFIERS`. An empty
624
634
  * list means the project uses none. */
625
635
  qualifiers?: SpeakerQualifier[];
636
+ /** How a character's label reads with a qualifier, in the source language: `{name}` and `{qualifier}` filled
637
+ * in. Absent = `DEFAULT_SPEAKER_PATTERN`. Localised as the `speaker:pattern` string. */
638
+ speakerPattern?: string;
626
639
  /** The project's default `padAfter` for every line and text beat that sets none and has no default above it.
627
640
  * Absent = `DEFAULT_PAD_AFTER`. */
628
641
  padAfterDefault?: number;
@@ -676,6 +689,19 @@ declare function castStringKey(name: string): string;
676
689
  /** The project-level loc-string key for a speaker qualifier's shown name, e.g. `qualifier:vo`. Resolved as a
677
690
  * cast display name is: the active locale, then the default locale, then the authored `name`. */
678
691
  declare function qualifierStringKey(gameId: string): string;
692
+ /** The project-level loc-string key for one character's own label with one qualifier, e.g. `speaker:PLAYER:vo`:
693
+ * only for a pair someone has overridden (`CastMember.qualifierLabels`). Every other pair's label is built from
694
+ * the speaker pattern. */
695
+ declare function speakerLabelKey(name: string, gameId: string): string;
696
+ /** The project-level loc-string key for the speaker pattern, the label a character shows with a qualifier. */
697
+ declare const SPEAKER_PATTERN_KEY = "speaker:pattern";
698
+ /** The speaker pattern a project starts with: `You (V.O.)`. `{name}` and `{qualifier}` are plain placeholders,
699
+ * never run through line interpolation. */
700
+ declare const DEFAULT_SPEAKER_PATTERN = "{name} ({qualifier})";
701
+ /** A qualified speaker's label: the pattern with its placeholders filled. Each placeholder is replaced
702
+ * wherever it appears; nothing else in the pattern is read. The one composition rule, on every runtime and
703
+ * in Patterpad. */
704
+ declare function formatSpeakerLabel(pattern: string, name: string, qualifier: string): string;
679
705
  /** Typed documentation annotation line (spec §18). */
680
706
  interface DocLine {
681
707
  /** The documentation CLASS (a `DocumentationClass.name`) - routes export
@@ -991,6 +1017,9 @@ interface Bundle {
991
1017
  /** The speaker qualifiers the content uses, in the project's order. Absent when no line has one, so a
992
1018
  * bundle without qualifiers is exactly as before. */
993
1019
  qualifiers?: BundleQualifier[];
1020
+ /** The project's speaker pattern, the unlocalised fallback for a qualified speaker's label. Absent when the
1021
+ * project keeps `DEFAULT_SPEAKER_PATTERN` or uses no qualifiers. */
1022
+ speakerPattern?: string;
994
1023
  /** The project's own `padAfterDefault`, when it sets one; absent = `DEFAULT_PAD_AFTER`. */
995
1024
  padAfterDefault?: number;
996
1025
  properties?: PropertyDecl[];
@@ -1076,6 +1105,8 @@ interface SavedChoicePrompt {
1076
1105
  direction?: string;
1077
1106
  qualifier?: string;
1078
1107
  qualifierName?: string;
1108
+ /** The speaker's label as the choice showed it. Absent in a save written before it existed. */
1109
+ speakerLabel?: string;
1079
1110
  /** The prompt's resolved pause (line padding). Absent in a save written before it existed. */
1080
1111
  padAfter?: number;
1081
1112
  }
@@ -1192,4 +1223,4 @@ declare const PROJECT_SCHEMA = "patter/project@0";
1192
1223
  declare const STRINGS_SCHEMA = "patter/strings@0";
1193
1224
  declare const AUTHORING_SCHEMA = "patter/authoring@0";
1194
1225
 
1195
- export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectFile, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, usedQualifiers, walkNodes };
1226
+ export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_SPEAKER_PATTERN, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectFile, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, SPEAKER_PATTERN_KEY, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, formatSpeakerLabel, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, speakerLabelKey, usedQualifiers, usedSpeakerPairs, walkNodes };
package/dist/index.d.ts CHANGED
@@ -352,6 +352,10 @@ interface CastMember {
352
352
  /** The voice actor cast for this character, if known - surfaced in the VO script export (spec §16).
353
353
  * Authoring-only (not shipped in the runtime bundle). */
354
354
  actor?: string;
355
+ /** This character's own label with a qualifier, by the qualifier's `gameId`, in the source language
356
+ * (`{ vo: "Your thoughts" }`), where the project's speaker pattern shouldn't apply. Localised as the
357
+ * `speaker:<name>:<gameId>` string (`speakerLabelKey`). Absent for every pair that follows the pattern. */
358
+ qualifierLabels?: Record<string, string>;
355
359
  gameData?: GameData;
356
360
  }
357
361
  /** A speaker qualifier (a screenplay's character extension): `V.O.`, `O.S.`, `RADIO`, or the project's own.
@@ -372,6 +376,12 @@ declare const DEFAULT_QUALIFIERS: SpeakerQualifier[];
372
376
  declare function projectQualifiers(project: Pick<ProjectFile, "qualifiers">): SpeakerQualifier[];
373
377
  /** The project's qualifiers that some line (or line prompt) in these scenes uses, in the project's order. */
374
378
  declare function usedQualifiers(project: Pick<ProjectFile, "qualifiers">, scenes: Scene[]): SpeakerQualifier[];
379
+ /** Every character and qualifier pair the lines use (a line's speaker with its qualifier, option prompts
380
+ * included), once each, in the order first met. A pair's label is shown only for these. */
381
+ declare function usedSpeakerPairs(scenes: Scene[]): {
382
+ character: string;
383
+ qualifier: string;
384
+ }[];
375
385
  /** The built-in `padAfter`, in seconds, for a project that sets no default of its own: the same on every runtime
376
386
  * and in Patterpad's Play window (design/proposals/line-padding.md section 4). */
377
387
  declare const DEFAULT_PAD_AFTER = 0.6;
@@ -623,6 +633,9 @@ interface ProjectFile {
623
633
  /** The speaker qualifiers a line can pick from, in display order; default `DEFAULT_QUALIFIERS`. An empty
624
634
  * list means the project uses none. */
625
635
  qualifiers?: SpeakerQualifier[];
636
+ /** How a character's label reads with a qualifier, in the source language: `{name}` and `{qualifier}` filled
637
+ * in. Absent = `DEFAULT_SPEAKER_PATTERN`. Localised as the `speaker:pattern` string. */
638
+ speakerPattern?: string;
626
639
  /** The project's default `padAfter` for every line and text beat that sets none and has no default above it.
627
640
  * Absent = `DEFAULT_PAD_AFTER`. */
628
641
  padAfterDefault?: number;
@@ -676,6 +689,19 @@ declare function castStringKey(name: string): string;
676
689
  /** The project-level loc-string key for a speaker qualifier's shown name, e.g. `qualifier:vo`. Resolved as a
677
690
  * cast display name is: the active locale, then the default locale, then the authored `name`. */
678
691
  declare function qualifierStringKey(gameId: string): string;
692
+ /** The project-level loc-string key for one character's own label with one qualifier, e.g. `speaker:PLAYER:vo`:
693
+ * only for a pair someone has overridden (`CastMember.qualifierLabels`). Every other pair's label is built from
694
+ * the speaker pattern. */
695
+ declare function speakerLabelKey(name: string, gameId: string): string;
696
+ /** The project-level loc-string key for the speaker pattern, the label a character shows with a qualifier. */
697
+ declare const SPEAKER_PATTERN_KEY = "speaker:pattern";
698
+ /** The speaker pattern a project starts with: `You (V.O.)`. `{name}` and `{qualifier}` are plain placeholders,
699
+ * never run through line interpolation. */
700
+ declare const DEFAULT_SPEAKER_PATTERN = "{name} ({qualifier})";
701
+ /** A qualified speaker's label: the pattern with its placeholders filled. Each placeholder is replaced
702
+ * wherever it appears; nothing else in the pattern is read. The one composition rule, on every runtime and
703
+ * in Patterpad. */
704
+ declare function formatSpeakerLabel(pattern: string, name: string, qualifier: string): string;
679
705
  /** Typed documentation annotation line (spec §18). */
680
706
  interface DocLine {
681
707
  /** The documentation CLASS (a `DocumentationClass.name`) - routes export
@@ -991,6 +1017,9 @@ interface Bundle {
991
1017
  /** The speaker qualifiers the content uses, in the project's order. Absent when no line has one, so a
992
1018
  * bundle without qualifiers is exactly as before. */
993
1019
  qualifiers?: BundleQualifier[];
1020
+ /** The project's speaker pattern, the unlocalised fallback for a qualified speaker's label. Absent when the
1021
+ * project keeps `DEFAULT_SPEAKER_PATTERN` or uses no qualifiers. */
1022
+ speakerPattern?: string;
994
1023
  /** The project's own `padAfterDefault`, when it sets one; absent = `DEFAULT_PAD_AFTER`. */
995
1024
  padAfterDefault?: number;
996
1025
  properties?: PropertyDecl[];
@@ -1076,6 +1105,8 @@ interface SavedChoicePrompt {
1076
1105
  direction?: string;
1077
1106
  qualifier?: string;
1078
1107
  qualifierName?: string;
1108
+ /** The speaker's label as the choice showed it. Absent in a save written before it existed. */
1109
+ speakerLabel?: string;
1079
1110
  /** The prompt's resolved pause (line padding). Absent in a save written before it existed. */
1080
1111
  padAfter?: number;
1081
1112
  }
@@ -1192,4 +1223,4 @@ declare const PROJECT_SCHEMA = "patter/project@0";
1192
1223
  declare const STRINGS_SCHEMA = "patter/strings@0";
1193
1224
  declare const AUTHORING_SCHEMA = "patter/authoring@0";
1194
1225
 
1195
- export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectFile, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, usedQualifiers, walkNodes };
1226
+ export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleQualifier, COMMON_GENDERS, type CaptionDelimiters, type CastMember, type Comment, type CommentMessage, type CommentRange, type CompiledBlock, type CompiledEffect, type CompiledGroup, type CompiledScene, type CompiledSnippet, type CoverageDriver, DEFAULT_CAPTION_CHARACTER, DEFAULT_CAPTION_DELIMITERS, DEFAULT_DOCUMENTATION_CLASSES, DEFAULT_LAYOUT, DEFAULT_PAD_AFTER, DEFAULT_QUALIFIERS, DEFAULT_RECORDING_STATUSES, DEFAULT_SPEAKER_PATTERN, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type FlowSnapshotV2, type FlowState, type FlowValues, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type GrammaticalGender, type Group, HANDOFF_SCHEMA, type HandoffFile, type HandoffImport, type HandoffLine, type HandoffRow, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectFile, type PromptBeat, type PropertyDecl, type PropertyType, RERECORD_STATUS, RERECORD_STATUS_DECL, type RecordingFolder, type RecordingStatusDecl, SAVE_SCHEMA, SAVE_VERSION, SPEAKER_PATTERN_KEY, STRINGS_SCHEMA, type SaveEnvelope, type SaveGame, type SaveGameV2, type SavedChoice, type SavedChoiceOption, type SavedChoicePrompt, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SelectorSnapshot, type SequenceOptions, type Snippet, type SpeakerQualifier, type StackFrame, type Suggestion, type SuggestionSource, type TextBeat, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, formatSpeakerLabel, gameIdify, isContentlessBeat, isValidGameId, projectLayout, projectQualifiers, qualifierStringKey, speakerLabelKey, usedQualifiers, usedSpeakerPairs, walkNodes };
package/dist/index.js CHANGED
@@ -53,6 +53,29 @@ function usedQualifiers(project, scenes) {
53
53
  }
54
54
  return projectQualifiers(project).filter((q) => used.has(q.gameId));
55
55
  }
56
+ function usedSpeakerPairs(scenes) {
57
+ const seen = /* @__PURE__ */ new Set();
58
+ const out = [];
59
+ const add = (beat) => {
60
+ if (!beat.character || !beat.qualifier) return;
61
+ const key = `${beat.character}\0${beat.qualifier}`;
62
+ if (seen.has(key)) return;
63
+ seen.add(key);
64
+ out.push({ character: beat.character, qualifier: beat.qualifier });
65
+ };
66
+ for (const scene of scenes) {
67
+ for (const block of scene.blocks) {
68
+ walkNodes(block.children, (node) => {
69
+ if (node.type === "group") {
70
+ if (node.prompt?.kind === "line") add(node.prompt);
71
+ return;
72
+ }
73
+ for (const beat of node.beats ?? []) if (beat.kind === "line") add(beat);
74
+ });
75
+ }
76
+ }
77
+ return out;
78
+ }
56
79
  var DEFAULT_PAD_AFTER = 0.6;
57
80
  var PAD_AFTER_MIN = -10;
58
81
  var PAD_AFTER_MAX = 60;
@@ -104,6 +127,14 @@ function castStringKey(name) {
104
127
  function qualifierStringKey(gameId) {
105
128
  return `qualifier:${gameId}`;
106
129
  }
130
+ function speakerLabelKey(name, gameId) {
131
+ return `speaker:${name}:${gameId}`;
132
+ }
133
+ var SPEAKER_PATTERN_KEY = "speaker:pattern";
134
+ var DEFAULT_SPEAKER_PATTERN = "{name} ({qualifier})";
135
+ function formatSpeakerLabel(pattern, name, qualifier) {
136
+ return pattern.split("{name}").map((part) => part.split("{qualifier}").join(qualifier)).join(name);
137
+ }
107
138
  var HANDOFF_SCHEMA = "patter/handoff@0";
108
139
  var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
109
140
  var DEFAULT_CAPTION_CHARACTER = "SFX";
@@ -127,6 +158,7 @@ export {
127
158
  DEFAULT_PAD_AFTER,
128
159
  DEFAULT_QUALIFIERS,
129
160
  DEFAULT_RECORDING_STATUSES,
161
+ DEFAULT_SPEAKER_PATTERN,
130
162
  DEFAULT_WRITING_STATUSES,
131
163
  FLOW_SCHEMA,
132
164
  HANDOFF_SCHEMA,
@@ -139,10 +171,12 @@ export {
139
171
  RESERVED_PROPERTY_NAMES,
140
172
  SAVE_SCHEMA,
141
173
  SAVE_VERSION,
174
+ SPEAKER_PATTERN_KEY,
142
175
  STRINGS_SCHEMA,
143
176
  castStringKey,
144
177
  deriveRecordingFolders,
145
178
  effectiveGameId,
179
+ formatSpeakerLabel,
146
180
  gameIdify,
147
181
  isCaseOnlyPropertyName,
148
182
  isContentlessBeat,
@@ -152,6 +186,8 @@ export {
152
186
  projectQualifiers,
153
187
  propertyNameify,
154
188
  qualifierStringKey,
189
+ speakerLabelKey,
155
190
  usedQualifiers,
191
+ usedSpeakerPairs,
156
192
  walkNodes
157
193
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/model",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Patter data-model types: source (scene/block/group/snippet/beat/jump), project, locale, authoring. The shape source-of-truth.",
5
5
  "type": "module",
6
6
  "license": "MIT",