@patterkit/model 0.20.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/dist/index.cjs +22 -11
- package/dist/index.d.cts +57 -39
- package/dist/index.d.ts +57 -39
- package/dist/index.js +14 -7
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -36,6 +36,9 @@ __export(index_exports, {
|
|
|
36
36
|
EXTENSION_SHARD: () => EXTENSION_SHARD,
|
|
37
37
|
FLOW_SCHEMA: () => FLOW_SCHEMA,
|
|
38
38
|
HANDOFF_SCHEMA: () => HANDOFF_SCHEMA,
|
|
39
|
+
LINE_CHARS_PER_SECOND: () => LINE_CHARS_PER_SECOND,
|
|
40
|
+
LINE_DURATION_MAX: () => LINE_DURATION_MAX,
|
|
41
|
+
LINE_DURATION_MIN: () => LINE_DURATION_MIN,
|
|
39
42
|
MANIFEST_EXTENSION: () => MANIFEST_EXTENSION,
|
|
40
43
|
PAD_AFTER_MAX: () => PAD_AFTER_MAX,
|
|
41
44
|
PAD_AFTER_MIN: () => PAD_AFTER_MIN,
|
|
@@ -53,12 +56,13 @@ __export(index_exports, {
|
|
|
53
56
|
castStringKey: () => castStringKey,
|
|
54
57
|
deriveRecordingFolders: () => deriveRecordingFolders,
|
|
55
58
|
effectiveGameId: () => effectiveGameId,
|
|
59
|
+
estimateLineDuration: () => estimateLineDuration,
|
|
56
60
|
formatSpeakerLabel: () => formatSpeakerLabel,
|
|
57
|
-
gameIdify: () => gameIdify,
|
|
61
|
+
gameIdify: () => import_toolkit.gameIdify,
|
|
58
62
|
isCaseOnlyPropertyName: () => import_expr.isCaseOnlyPropertyName,
|
|
59
63
|
isContentlessBeat: () => isContentlessBeat,
|
|
60
64
|
isDialogueScene: () => isDialogueScene,
|
|
61
|
-
isValidGameId: () => isValidGameId,
|
|
65
|
+
isValidGameId: () => import_toolkit.isValidGameId,
|
|
62
66
|
isValidPropertyName: () => import_expr.isValidPropertyName,
|
|
63
67
|
projectLayout: () => projectLayout,
|
|
64
68
|
projectQualifiers: () => projectQualifiers,
|
|
@@ -73,6 +77,7 @@ __export(index_exports, {
|
|
|
73
77
|
walkNodes: () => walkNodes
|
|
74
78
|
});
|
|
75
79
|
module.exports = __toCommonJS(index_exports);
|
|
80
|
+
var import_toolkit = require("@wildwinter/toolkit");
|
|
76
81
|
var import_expr = require("@wildwinter/expr");
|
|
77
82
|
function walkNodes(nodes, visit) {
|
|
78
83
|
for (const node of nodes) {
|
|
@@ -88,15 +93,9 @@ function isContentlessBeat(beat, hasText) {
|
|
|
88
93
|
if (beat.tags && beat.tags.length > 0) return false;
|
|
89
94
|
return beat.kind === "text" || !beat.character && !beat.direction;
|
|
90
95
|
}
|
|
91
|
-
function gameIdify(text) {
|
|
92
|
-
return text.toLowerCase().replace(/['’]/g, "").replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
93
|
-
}
|
|
94
|
-
function isValidGameId(gameId) {
|
|
95
|
-
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);
|
|
96
|
-
}
|
|
97
96
|
function effectiveGameId(entity) {
|
|
98
97
|
const g = entity.gameId?.trim();
|
|
99
|
-
return g ? g : gameIdify(entity.name);
|
|
98
|
+
return g ? g : (0, import_toolkit.gameIdify)(entity.name);
|
|
100
99
|
}
|
|
101
100
|
function sceneAddressIndex(scenes) {
|
|
102
101
|
const byAddress = /* @__PURE__ */ new Map();
|
|
@@ -177,6 +176,14 @@ function textIsAction(project, scene) {
|
|
|
177
176
|
var DEFAULT_PAD_AFTER = 0.6;
|
|
178
177
|
var PAD_AFTER_MIN = -10;
|
|
179
178
|
var PAD_AFTER_MAX = 60;
|
|
179
|
+
var LINE_CHARS_PER_SECOND = 14;
|
|
180
|
+
var LINE_DURATION_MIN = 1.5;
|
|
181
|
+
var LINE_DURATION_MAX = 18;
|
|
182
|
+
function estimateLineDuration(text, _locale) {
|
|
183
|
+
let count = 0;
|
|
184
|
+
for (const ch of text ?? "") if (ch !== "\xAD") count++;
|
|
185
|
+
return Math.min(Math.max(count / LINE_CHARS_PER_SECOND, LINE_DURATION_MIN), LINE_DURATION_MAX);
|
|
186
|
+
}
|
|
180
187
|
var DEFAULT_WRITING_STATUSES = [
|
|
181
188
|
{ name: "stub", colour: 0 },
|
|
182
189
|
// red
|
|
@@ -205,7 +212,7 @@ function deriveRecordingFolders(audioRoot, statuses) {
|
|
|
205
212
|
const root = audioRoot?.trim();
|
|
206
213
|
return statuses.map((s, i) => {
|
|
207
214
|
if (i === 0 || !root) return { name: s.name };
|
|
208
|
-
return { name: s.name, folder: `${root}/${gameIdify(s.name)}` };
|
|
215
|
+
return { name: s.name, folder: `${root}/${(0, import_toolkit.gameIdify)(s.name)}` };
|
|
209
216
|
});
|
|
210
217
|
}
|
|
211
218
|
var DEFAULT_DOCUMENTATION_CLASSES = [
|
|
@@ -237,7 +244,7 @@ var HANDOFF_SCHEMA = "patter/handoff@0";
|
|
|
237
244
|
var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
|
|
238
245
|
var DEFAULT_CAPTION_CHARACTER = "SFX";
|
|
239
246
|
var SAVE_SCHEMA = "patter/save@0";
|
|
240
|
-
var SAVE_VERSION =
|
|
247
|
+
var SAVE_VERSION = 4;
|
|
241
248
|
var DEFAULT_LAYOUT = { flow: "scenes/", strings: "loc/", authoring: "authoring/", extensions: "extensions/" };
|
|
242
249
|
function projectLayout(project) {
|
|
243
250
|
return { ...DEFAULT_LAYOUT, ...project.layout };
|
|
@@ -270,6 +277,9 @@ var UNRESOLVED_KEY = "unresolved";
|
|
|
270
277
|
EXTENSION_SHARD,
|
|
271
278
|
FLOW_SCHEMA,
|
|
272
279
|
HANDOFF_SCHEMA,
|
|
280
|
+
LINE_CHARS_PER_SECOND,
|
|
281
|
+
LINE_DURATION_MAX,
|
|
282
|
+
LINE_DURATION_MIN,
|
|
273
283
|
MANIFEST_EXTENSION,
|
|
274
284
|
PAD_AFTER_MAX,
|
|
275
285
|
PAD_AFTER_MIN,
|
|
@@ -287,6 +297,7 @@ var UNRESOLVED_KEY = "unresolved";
|
|
|
287
297
|
castStringKey,
|
|
288
298
|
deriveRecordingFolders,
|
|
289
299
|
effectiveGameId,
|
|
300
|
+
estimateLineDuration,
|
|
290
301
|
formatSpeakerLabel,
|
|
291
302
|
gameIdify,
|
|
292
303
|
isCaseOnlyPropertyName,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AstNode } from '@wildwinter/expr';
|
|
2
2
|
export { RESERVED_PROPERTY_NAMES, isCaseOnlyPropertyName, isValidPropertyName, propertyNameify } from '@wildwinter/expr';
|
|
3
|
+
export { gameIdify, isValidGameId } from '@wildwinter/toolkit';
|
|
3
4
|
|
|
4
5
|
type ScalarValue = boolean | number | string | string[];
|
|
5
6
|
/** Developer-defined host metadata (spec §17). Opaque to Patter. */
|
|
@@ -246,11 +247,6 @@ declare function walkNodes<N extends {
|
|
|
246
247
|
* just `{ jump }` with zero beats - which is valid.
|
|
247
248
|
*/
|
|
248
249
|
declare function isContentlessBeat(beat: Beat, hasText: boolean): boolean;
|
|
249
|
-
/** Slugify a name into a hyphen-form game id: lowercase, drop apostrophes, runs of
|
|
250
|
-
* other punctuation -> a single hyphen, collapse repeats, no leading / trailing hyphen. */
|
|
251
|
-
declare function gameIdify(text: string): string;
|
|
252
|
-
/** A valid authored game id: lowercase alphanumerics + hyphens, no leading / trailing hyphen. */
|
|
253
|
-
declare function isValidGameId(gameId: string): boolean;
|
|
254
250
|
|
|
255
251
|
/** The effective address: the explicit `gameId` if pinned, else derived from `name`. */
|
|
256
252
|
declare function effectiveGameId(entity: {
|
|
@@ -392,6 +388,11 @@ interface CastMember {
|
|
|
392
388
|
* (`{ vo: "Your thoughts" }`), where the project's speaker pattern shouldn't apply. Localised as the
|
|
393
389
|
* `speaker:<name>:<gameId>` string (`speakerLabelKey`). Absent for every pair that follows the pattern. */
|
|
394
390
|
qualifierLabels?: Record<string, string>;
|
|
391
|
+
/** The character's colour in the editors (their cue, the play window, the readable script): a slot of the
|
|
392
|
+
* family's curated palette, 0 to 11. Absent, the colour comes from a hash of the name, so a cast needs no
|
|
393
|
+
* setup; renaming a character stamps the old name's slot here, so they keep their colour. Authoring only
|
|
394
|
+
* (not shipped in the runtime bundle). */
|
|
395
|
+
colour?: number;
|
|
395
396
|
gameData?: GameData;
|
|
396
397
|
}
|
|
397
398
|
/** A speaker qualifier (a screenplay's character extension): `V.O.`, `O.S.`, `RADIO`, or the project's own.
|
|
@@ -430,17 +431,28 @@ declare const DEFAULT_PAD_AFTER = 0.6;
|
|
|
430
431
|
/** The range validation keeps a `padAfter` or `padAfterDefault` within, in seconds. */
|
|
431
432
|
declare const PAD_AFTER_MIN = -10;
|
|
432
433
|
declare const PAD_AFTER_MAX = 60;
|
|
434
|
+
/** The reading rate a line with no recording is timed by, in characters a second (`estimateLineDuration`). */
|
|
435
|
+
declare const LINE_CHARS_PER_SECOND = 14;
|
|
436
|
+
/** The shortest and longest a line with no recording lasts, in seconds (`estimateLineDuration`). */
|
|
437
|
+
declare const LINE_DURATION_MIN = 1.5;
|
|
438
|
+
declare const LINE_DURATION_MAX = 18;
|
|
439
|
+
/** How long a line with no recording lasts, in seconds: its characters at 14 a second, clamped to 1.5 to 18
|
|
440
|
+
* seconds. A character is a Unicode code point (one outside the Basic Multilingual Plane counts once), and soft
|
|
441
|
+
* hyphens (U+00AD) aren't counted. This is the one rule every Patter tool and runtime times a line by: Patterpad's
|
|
442
|
+
* Play window, the playable HTML page, and the Unity, Unreal, and Godot runtimes, which the conformance corpus
|
|
443
|
+
* holds to the same numbers. `locale` is reserved for per-language reading rates and is unused for now. */
|
|
444
|
+
declare function estimateLineDuration(text: string, _locale?: string): number;
|
|
433
445
|
/** A qualifier as it reaches a compiled bundle: its `gameId` and authored name (the unlocalised fallback). */
|
|
434
446
|
interface BundleQualifier {
|
|
435
447
|
gameId: string;
|
|
436
448
|
name: string;
|
|
437
449
|
}
|
|
438
|
-
/** The cast as it reaches a compiled bundle: the player-facing fields only. `notes`, `actor
|
|
439
|
-
* `gender` are authoring / production / translation context, and the compiler drops them, so a shipped
|
|
450
|
+
/** The cast as it reaches a compiled bundle: the player-facing fields only. `notes`, `actor`,
|
|
451
|
+
* `gender` and `colour` are authoring / production / translation context, and the compiler drops them, so a shipped
|
|
440
452
|
* game never carries a real person's name or a writer's private notes. The compiler copies the shipping
|
|
441
453
|
* fields across explicitly (an allow-list), which is what actually keeps a new authoring field out of
|
|
442
454
|
* the bundle; this type states the resulting contract for anyone reading a bundle. */
|
|
443
|
-
type BundleCastMember = Omit<CastMember, "notes" | "actor" | "gender">;
|
|
455
|
+
type BundleCastMember = Omit<CastMember, "notes" | "actor" | "gender" | "colour">;
|
|
444
456
|
/** A node TYPE that can carry author-defined gameData fields (the gameData schema, project-level).
|
|
445
457
|
* Beat kinds map straight through: dialogue = `line`, text = `text`, game event = `gameEvent`.
|
|
446
458
|
* `option` is a choice's option: a group that is a direct child of a `choice` group, whose gameData the
|
|
@@ -1091,6 +1103,13 @@ interface Bundle {
|
|
|
1091
1103
|
default: string;
|
|
1092
1104
|
included: string[];
|
|
1093
1105
|
};
|
|
1106
|
+
/** Where a flow opened without a scene starts: the project's authored start (scene id, and block id when
|
|
1107
|
+
* it names one). Absent when the project sets none, or names one that has gone: every runtime then starts
|
|
1108
|
+
* at the first scene. */
|
|
1109
|
+
start?: {
|
|
1110
|
+
scene: string;
|
|
1111
|
+
block?: string;
|
|
1112
|
+
};
|
|
1094
1113
|
/** Player-facing cast only: the compiler strips notes / actor / gender (see `BundleCastMember`). */
|
|
1095
1114
|
cast?: BundleCastMember[];
|
|
1096
1115
|
/** The speaker qualifiers the content uses, in the project's order. Absent when no line has one, so a
|
|
@@ -1154,7 +1173,7 @@ declare const DEFAULT_CAPTION_DELIMITERS: CaptionDelimiters;
|
|
|
1154
1173
|
declare const DEFAULT_CAPTION_CHARACTER = "SFX";
|
|
1155
1174
|
declare const SAVE_SCHEMA = "patter/save@0";
|
|
1156
1175
|
/** The `save.version` inside the envelope. Bumped only when a reader would MISREAD an older save. */
|
|
1157
|
-
declare const SAVE_VERSION =
|
|
1176
|
+
declare const SAVE_VERSION = 4;
|
|
1158
1177
|
/** The tagged envelope a host stores: `serializeState` writes it, `deserializeState` refuses anything else. */
|
|
1159
1178
|
interface SaveEnvelope {
|
|
1160
1179
|
schema: typeof SAVE_SCHEMA;
|
|
@@ -1181,8 +1200,11 @@ interface StackFrame {
|
|
|
1181
1200
|
* restore against an EDITED bundle re-finds the position by id. Absent when the frame sat at its
|
|
1182
1201
|
* container's end (no next child), or in an older save. */
|
|
1183
1202
|
nextId?: string;
|
|
1203
|
+
/** SNAPSHOT-ONLY: on a frame whose container is an option of a choice, that choice group's id, so a load
|
|
1204
|
+
* whose option has gone offers the choice again. Absent on every other frame, and in a version 3 save. */
|
|
1205
|
+
choiceId?: string;
|
|
1184
1206
|
}
|
|
1185
|
-
/**
|
|
1207
|
+
/** A prompt as the choice showed it: a replayed prompt still to be spoken (`FlowCursor.pendingPrompt`). */
|
|
1186
1208
|
interface SavedChoicePrompt {
|
|
1187
1209
|
kind: "line" | "text";
|
|
1188
1210
|
text: string;
|
|
@@ -1196,10 +1218,12 @@ interface SavedChoicePrompt {
|
|
|
1196
1218
|
/** The prompt's resolved pause (line padding). Absent in a save written before it existed. */
|
|
1197
1219
|
padAfter?: number;
|
|
1198
1220
|
}
|
|
1199
|
-
/** One option of a pending choice,
|
|
1221
|
+
/** One option of a pending choice: which option, and whether it could be taken, saved VERBATIM, since
|
|
1222
|
+
* re-deriving on load would re-evaluate conditions. Its prompt is not saved: it is built again in the
|
|
1223
|
+
* language in use each time the choice is delivered. (A version 3 save's option also has a `prompt`,
|
|
1224
|
+
* which a load discards.) */
|
|
1200
1225
|
interface SavedChoiceOption {
|
|
1201
1226
|
id: string;
|
|
1202
|
-
prompt?: SavedChoicePrompt;
|
|
1203
1227
|
eligible: boolean;
|
|
1204
1228
|
gameData?: GameData;
|
|
1205
1229
|
}
|
|
@@ -1215,17 +1239,21 @@ interface FlowCursor {
|
|
|
1215
1239
|
/** The continuation stack (call frames + the active block run), root first. */
|
|
1216
1240
|
stack: StackFrame[];
|
|
1217
1241
|
activeSnippetId: string | null;
|
|
1218
|
-
|
|
1242
|
+
/** The id of the next beat the active snippet delivers, so a load against an edited bundle finds its
|
|
1243
|
+
* place by id. Absent when no snippet is active or the active one has delivered every beat. */
|
|
1244
|
+
beatId?: string;
|
|
1245
|
+
/** Version 3 only, read and never written: the index of the next beat in the active snippet. */
|
|
1246
|
+
beatIndex?: number;
|
|
1219
1247
|
/** Present (non-null) iff the flow is stopped at a choice. */
|
|
1220
1248
|
pendingChoice: SavedChoice | null;
|
|
1221
1249
|
/** The chosen option owning a prompt still to be replayed (a save taken between choose + advance).
|
|
1222
1250
|
* Optional / null in older saves -> no pending prompt. */
|
|
1223
1251
|
pendingPromptOwnerId?: string | null;
|
|
1224
|
-
/** The prompt still to be replayed, exactly as the choice SHOWED it (text and speaker fields, the
|
|
1225
|
-
*
|
|
1226
|
-
* alongside a `pendingPromptOwnerId` (the choose -> advance window, with
|
|
1227
|
-
* absent otherwise. A save without it (written before Patterplay 0.18.0)
|
|
1228
|
-
* when it is delivered, as before. */
|
|
1252
|
+
/** The prompt still to be replayed, exactly as the choice SHOWED it (text and speaker fields), so the
|
|
1253
|
+
* replay after a load says what the player saw, in the language they saw it in: the one prompt a save
|
|
1254
|
+
* keeps as text. Written only alongside a `pendingPromptOwnerId` (the choose -> advance window, with
|
|
1255
|
+
* `replayPromptOnChoose`), and absent otherwise. A save without it (written before Patterplay 0.18.0)
|
|
1256
|
+
* resolves the owner's prompt beat when it is delivered, as before. */
|
|
1229
1257
|
pendingPrompt?: SavedChoicePrompt;
|
|
1230
1258
|
/** This flow's (non-shared) selector cursors, keyed by group id. */
|
|
1231
1259
|
selectors: Record<string, SelectorSnapshot>;
|
|
@@ -1238,10 +1266,10 @@ interface FlowSnapshot {
|
|
|
1238
1266
|
visits: Record<string, number>;
|
|
1239
1267
|
cursor: FlowCursor;
|
|
1240
1268
|
}
|
|
1241
|
-
/** A full resumable save-game (version
|
|
1269
|
+
/** A full resumable save-game (version 4): everything that is not a property, plus the registry's
|
|
1242
1270
|
* values when the engine made its own registry. */
|
|
1243
1271
|
interface SaveGame {
|
|
1244
|
-
version:
|
|
1272
|
+
version: typeof SAVE_VERSION;
|
|
1245
1273
|
/** The engine's own registry's values, keyed by registry key: present only when the engine made the
|
|
1246
1274
|
* registry itself (a standalone game). A game that passed a registry saves it once, beside this. */
|
|
1247
1275
|
registry?: EngineSave;
|
|
@@ -1252,17 +1280,18 @@ interface SaveGame {
|
|
|
1252
1280
|
/** Each live flow's snapshot, keyed by flow id. */
|
|
1253
1281
|
flows: Record<string, FlowSnapshot>;
|
|
1254
1282
|
}
|
|
1255
|
-
/**
|
|
1256
|
-
* `
|
|
1283
|
+
/** A version 3 save, still read: the same shape apart from the cursor (`beatIndex` for `beatId`, no
|
|
1284
|
+
* `choiceId` on its frames) and a pending choice's options (each with its prompt text, discarded). */
|
|
1285
|
+
interface SaveGameV3 extends Omit<SaveGame, "version"> {
|
|
1286
|
+
version: 3;
|
|
1287
|
+
}
|
|
1288
|
+
/** One flow's own property values, without its id: what a `FlowState` carries beside its snapshot. */
|
|
1257
1289
|
interface FlowValues {
|
|
1258
1290
|
/** The NOT-shared `@patter` globals, under token "patter". */
|
|
1259
1291
|
scopes: EngineSave;
|
|
1260
1292
|
/** Per-scene NOT-shared `@scene` bags (scene id -> name -> value). */
|
|
1261
1293
|
sceneBags: Record<string, Record<string, ScalarValue>>;
|
|
1262
1294
|
}
|
|
1263
|
-
/** A version 2 flow snapshot, which carried the flow's property values itself. Still read. */
|
|
1264
|
-
interface FlowSnapshotV2 extends FlowSnapshot, FlowValues {
|
|
1265
|
-
}
|
|
1266
1295
|
/**
|
|
1267
1296
|
* One flow taken out of an engine (`engine.captureFlow`) to be put back later (`engine.restoreFlow`), under the
|
|
1268
1297
|
* same id or another: its snapshot as a save writes it, and its own property values without its id. Plain data
|
|
@@ -1270,19 +1299,8 @@ interface FlowSnapshotV2 extends FlowSnapshot, FlowValues {
|
|
|
1270
1299
|
* visit counts, shared selectors) is never in it, so restoring one rewinds nothing else.
|
|
1271
1300
|
*/
|
|
1272
1301
|
interface FlowState extends FlowSnapshot, FlowValues {
|
|
1273
|
-
version
|
|
1274
|
-
|
|
1275
|
-
/** A version 2 save, from before the registry held the properties. Every runtime still reads it: its
|
|
1276
|
-
* property sections move into the registry (under the keys in the header above) as it loads. */
|
|
1277
|
-
interface SaveGameV2 {
|
|
1278
|
-
version: 2;
|
|
1279
|
-
/** Shared `@patter` globals (owned scope "patter"). */
|
|
1280
|
-
shared: EngineSave;
|
|
1281
|
-
sharedVisits: Record<string, number>;
|
|
1282
|
-
sharedSelectors: Record<string, SelectorSnapshot>;
|
|
1283
|
-
/** Shared, scene-namespaced `@scene` bags (scene id -> name -> value). */
|
|
1284
|
-
stageBags: Record<string, Record<string, ScalarValue>>;
|
|
1285
|
-
flows: Record<string, FlowSnapshotV2>;
|
|
1302
|
+
/** `SAVE_VERSION` when written; a version 3 state still restores. */
|
|
1303
|
+
version: typeof SAVE_VERSION | 3;
|
|
1286
1304
|
}
|
|
1287
1305
|
/** Where a project keeps its shards unless its `layout` says otherwise, relative to the project root. */
|
|
1288
1306
|
declare const DEFAULT_LAYOUT: {
|
|
@@ -1394,4 +1412,4 @@ interface ExtensionFieldDecl {
|
|
|
1394
1412
|
/** A node's extension values in a bundle: extension id -> field -> value, raw. */
|
|
1395
1413
|
type NodeExtensions = Record<string, Record<string, unknown>>;
|
|
1396
1414
|
|
|
1397
|
-
export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleExtension, 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, EXTENSIONS_DIR, EXTENSION_SCHEMA, EXTENSION_SHARD, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, type ExtensionFieldDecl, type ExtensionShard, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type
|
|
1415
|
+
export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleExtension, 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, EXTENSIONS_DIR, EXTENSION_SCHEMA, EXTENSION_SHARD, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, type ExtensionFieldDecl, type ExtensionShard, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, 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, LINE_CHARS_PER_SECOND, LINE_DURATION_MAX, LINE_DURATION_MIN, type LineBeat, type LocaleFile, MANIFEST_EXTENSION, type NodeExtensions, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_EXTENSION_STEM, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectExtension, type ProjectFile, type ProjectLayout, 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 SaveGameV3, 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, UNRESOLVED_KEY, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, estimateLineDuration, formatSpeakerLabel, isContentlessBeat, isDialogueScene, projectLayout, projectQualifiers, qualifierStringKey, resolveSceneRef, sceneAddressIndex, speakerLabelKey, textIsAction, usedQualifiers, usedSpeakerPairs, walkNodes };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AstNode } from '@wildwinter/expr';
|
|
2
2
|
export { RESERVED_PROPERTY_NAMES, isCaseOnlyPropertyName, isValidPropertyName, propertyNameify } from '@wildwinter/expr';
|
|
3
|
+
export { gameIdify, isValidGameId } from '@wildwinter/toolkit';
|
|
3
4
|
|
|
4
5
|
type ScalarValue = boolean | number | string | string[];
|
|
5
6
|
/** Developer-defined host metadata (spec §17). Opaque to Patter. */
|
|
@@ -246,11 +247,6 @@ declare function walkNodes<N extends {
|
|
|
246
247
|
* just `{ jump }` with zero beats - which is valid.
|
|
247
248
|
*/
|
|
248
249
|
declare function isContentlessBeat(beat: Beat, hasText: boolean): boolean;
|
|
249
|
-
/** Slugify a name into a hyphen-form game id: lowercase, drop apostrophes, runs of
|
|
250
|
-
* other punctuation -> a single hyphen, collapse repeats, no leading / trailing hyphen. */
|
|
251
|
-
declare function gameIdify(text: string): string;
|
|
252
|
-
/** A valid authored game id: lowercase alphanumerics + hyphens, no leading / trailing hyphen. */
|
|
253
|
-
declare function isValidGameId(gameId: string): boolean;
|
|
254
250
|
|
|
255
251
|
/** The effective address: the explicit `gameId` if pinned, else derived from `name`. */
|
|
256
252
|
declare function effectiveGameId(entity: {
|
|
@@ -392,6 +388,11 @@ interface CastMember {
|
|
|
392
388
|
* (`{ vo: "Your thoughts" }`), where the project's speaker pattern shouldn't apply. Localised as the
|
|
393
389
|
* `speaker:<name>:<gameId>` string (`speakerLabelKey`). Absent for every pair that follows the pattern. */
|
|
394
390
|
qualifierLabels?: Record<string, string>;
|
|
391
|
+
/** The character's colour in the editors (their cue, the play window, the readable script): a slot of the
|
|
392
|
+
* family's curated palette, 0 to 11. Absent, the colour comes from a hash of the name, so a cast needs no
|
|
393
|
+
* setup; renaming a character stamps the old name's slot here, so they keep their colour. Authoring only
|
|
394
|
+
* (not shipped in the runtime bundle). */
|
|
395
|
+
colour?: number;
|
|
395
396
|
gameData?: GameData;
|
|
396
397
|
}
|
|
397
398
|
/** A speaker qualifier (a screenplay's character extension): `V.O.`, `O.S.`, `RADIO`, or the project's own.
|
|
@@ -430,17 +431,28 @@ declare const DEFAULT_PAD_AFTER = 0.6;
|
|
|
430
431
|
/** The range validation keeps a `padAfter` or `padAfterDefault` within, in seconds. */
|
|
431
432
|
declare const PAD_AFTER_MIN = -10;
|
|
432
433
|
declare const PAD_AFTER_MAX = 60;
|
|
434
|
+
/** The reading rate a line with no recording is timed by, in characters a second (`estimateLineDuration`). */
|
|
435
|
+
declare const LINE_CHARS_PER_SECOND = 14;
|
|
436
|
+
/** The shortest and longest a line with no recording lasts, in seconds (`estimateLineDuration`). */
|
|
437
|
+
declare const LINE_DURATION_MIN = 1.5;
|
|
438
|
+
declare const LINE_DURATION_MAX = 18;
|
|
439
|
+
/** How long a line with no recording lasts, in seconds: its characters at 14 a second, clamped to 1.5 to 18
|
|
440
|
+
* seconds. A character is a Unicode code point (one outside the Basic Multilingual Plane counts once), and soft
|
|
441
|
+
* hyphens (U+00AD) aren't counted. This is the one rule every Patter tool and runtime times a line by: Patterpad's
|
|
442
|
+
* Play window, the playable HTML page, and the Unity, Unreal, and Godot runtimes, which the conformance corpus
|
|
443
|
+
* holds to the same numbers. `locale` is reserved for per-language reading rates and is unused for now. */
|
|
444
|
+
declare function estimateLineDuration(text: string, _locale?: string): number;
|
|
433
445
|
/** A qualifier as it reaches a compiled bundle: its `gameId` and authored name (the unlocalised fallback). */
|
|
434
446
|
interface BundleQualifier {
|
|
435
447
|
gameId: string;
|
|
436
448
|
name: string;
|
|
437
449
|
}
|
|
438
|
-
/** The cast as it reaches a compiled bundle: the player-facing fields only. `notes`, `actor
|
|
439
|
-
* `gender` are authoring / production / translation context, and the compiler drops them, so a shipped
|
|
450
|
+
/** The cast as it reaches a compiled bundle: the player-facing fields only. `notes`, `actor`,
|
|
451
|
+
* `gender` and `colour` are authoring / production / translation context, and the compiler drops them, so a shipped
|
|
440
452
|
* game never carries a real person's name or a writer's private notes. The compiler copies the shipping
|
|
441
453
|
* fields across explicitly (an allow-list), which is what actually keeps a new authoring field out of
|
|
442
454
|
* the bundle; this type states the resulting contract for anyone reading a bundle. */
|
|
443
|
-
type BundleCastMember = Omit<CastMember, "notes" | "actor" | "gender">;
|
|
455
|
+
type BundleCastMember = Omit<CastMember, "notes" | "actor" | "gender" | "colour">;
|
|
444
456
|
/** A node TYPE that can carry author-defined gameData fields (the gameData schema, project-level).
|
|
445
457
|
* Beat kinds map straight through: dialogue = `line`, text = `text`, game event = `gameEvent`.
|
|
446
458
|
* `option` is a choice's option: a group that is a direct child of a `choice` group, whose gameData the
|
|
@@ -1091,6 +1103,13 @@ interface Bundle {
|
|
|
1091
1103
|
default: string;
|
|
1092
1104
|
included: string[];
|
|
1093
1105
|
};
|
|
1106
|
+
/** Where a flow opened without a scene starts: the project's authored start (scene id, and block id when
|
|
1107
|
+
* it names one). Absent when the project sets none, or names one that has gone: every runtime then starts
|
|
1108
|
+
* at the first scene. */
|
|
1109
|
+
start?: {
|
|
1110
|
+
scene: string;
|
|
1111
|
+
block?: string;
|
|
1112
|
+
};
|
|
1094
1113
|
/** Player-facing cast only: the compiler strips notes / actor / gender (see `BundleCastMember`). */
|
|
1095
1114
|
cast?: BundleCastMember[];
|
|
1096
1115
|
/** The speaker qualifiers the content uses, in the project's order. Absent when no line has one, so a
|
|
@@ -1154,7 +1173,7 @@ declare const DEFAULT_CAPTION_DELIMITERS: CaptionDelimiters;
|
|
|
1154
1173
|
declare const DEFAULT_CAPTION_CHARACTER = "SFX";
|
|
1155
1174
|
declare const SAVE_SCHEMA = "patter/save@0";
|
|
1156
1175
|
/** The `save.version` inside the envelope. Bumped only when a reader would MISREAD an older save. */
|
|
1157
|
-
declare const SAVE_VERSION =
|
|
1176
|
+
declare const SAVE_VERSION = 4;
|
|
1158
1177
|
/** The tagged envelope a host stores: `serializeState` writes it, `deserializeState` refuses anything else. */
|
|
1159
1178
|
interface SaveEnvelope {
|
|
1160
1179
|
schema: typeof SAVE_SCHEMA;
|
|
@@ -1181,8 +1200,11 @@ interface StackFrame {
|
|
|
1181
1200
|
* restore against an EDITED bundle re-finds the position by id. Absent when the frame sat at its
|
|
1182
1201
|
* container's end (no next child), or in an older save. */
|
|
1183
1202
|
nextId?: string;
|
|
1203
|
+
/** SNAPSHOT-ONLY: on a frame whose container is an option of a choice, that choice group's id, so a load
|
|
1204
|
+
* whose option has gone offers the choice again. Absent on every other frame, and in a version 3 save. */
|
|
1205
|
+
choiceId?: string;
|
|
1184
1206
|
}
|
|
1185
|
-
/**
|
|
1207
|
+
/** A prompt as the choice showed it: a replayed prompt still to be spoken (`FlowCursor.pendingPrompt`). */
|
|
1186
1208
|
interface SavedChoicePrompt {
|
|
1187
1209
|
kind: "line" | "text";
|
|
1188
1210
|
text: string;
|
|
@@ -1196,10 +1218,12 @@ interface SavedChoicePrompt {
|
|
|
1196
1218
|
/** The prompt's resolved pause (line padding). Absent in a save written before it existed. */
|
|
1197
1219
|
padAfter?: number;
|
|
1198
1220
|
}
|
|
1199
|
-
/** One option of a pending choice,
|
|
1221
|
+
/** One option of a pending choice: which option, and whether it could be taken, saved VERBATIM, since
|
|
1222
|
+
* re-deriving on load would re-evaluate conditions. Its prompt is not saved: it is built again in the
|
|
1223
|
+
* language in use each time the choice is delivered. (A version 3 save's option also has a `prompt`,
|
|
1224
|
+
* which a load discards.) */
|
|
1200
1225
|
interface SavedChoiceOption {
|
|
1201
1226
|
id: string;
|
|
1202
|
-
prompt?: SavedChoicePrompt;
|
|
1203
1227
|
eligible: boolean;
|
|
1204
1228
|
gameData?: GameData;
|
|
1205
1229
|
}
|
|
@@ -1215,17 +1239,21 @@ interface FlowCursor {
|
|
|
1215
1239
|
/** The continuation stack (call frames + the active block run), root first. */
|
|
1216
1240
|
stack: StackFrame[];
|
|
1217
1241
|
activeSnippetId: string | null;
|
|
1218
|
-
|
|
1242
|
+
/** The id of the next beat the active snippet delivers, so a load against an edited bundle finds its
|
|
1243
|
+
* place by id. Absent when no snippet is active or the active one has delivered every beat. */
|
|
1244
|
+
beatId?: string;
|
|
1245
|
+
/** Version 3 only, read and never written: the index of the next beat in the active snippet. */
|
|
1246
|
+
beatIndex?: number;
|
|
1219
1247
|
/** Present (non-null) iff the flow is stopped at a choice. */
|
|
1220
1248
|
pendingChoice: SavedChoice | null;
|
|
1221
1249
|
/** The chosen option owning a prompt still to be replayed (a save taken between choose + advance).
|
|
1222
1250
|
* Optional / null in older saves -> no pending prompt. */
|
|
1223
1251
|
pendingPromptOwnerId?: string | null;
|
|
1224
|
-
/** The prompt still to be replayed, exactly as the choice SHOWED it (text and speaker fields, the
|
|
1225
|
-
*
|
|
1226
|
-
* alongside a `pendingPromptOwnerId` (the choose -> advance window, with
|
|
1227
|
-
* absent otherwise. A save without it (written before Patterplay 0.18.0)
|
|
1228
|
-
* when it is delivered, as before. */
|
|
1252
|
+
/** The prompt still to be replayed, exactly as the choice SHOWED it (text and speaker fields), so the
|
|
1253
|
+
* replay after a load says what the player saw, in the language they saw it in: the one prompt a save
|
|
1254
|
+
* keeps as text. Written only alongside a `pendingPromptOwnerId` (the choose -> advance window, with
|
|
1255
|
+
* `replayPromptOnChoose`), and absent otherwise. A save without it (written before Patterplay 0.18.0)
|
|
1256
|
+
* resolves the owner's prompt beat when it is delivered, as before. */
|
|
1229
1257
|
pendingPrompt?: SavedChoicePrompt;
|
|
1230
1258
|
/** This flow's (non-shared) selector cursors, keyed by group id. */
|
|
1231
1259
|
selectors: Record<string, SelectorSnapshot>;
|
|
@@ -1238,10 +1266,10 @@ interface FlowSnapshot {
|
|
|
1238
1266
|
visits: Record<string, number>;
|
|
1239
1267
|
cursor: FlowCursor;
|
|
1240
1268
|
}
|
|
1241
|
-
/** A full resumable save-game (version
|
|
1269
|
+
/** A full resumable save-game (version 4): everything that is not a property, plus the registry's
|
|
1242
1270
|
* values when the engine made its own registry. */
|
|
1243
1271
|
interface SaveGame {
|
|
1244
|
-
version:
|
|
1272
|
+
version: typeof SAVE_VERSION;
|
|
1245
1273
|
/** The engine's own registry's values, keyed by registry key: present only when the engine made the
|
|
1246
1274
|
* registry itself (a standalone game). A game that passed a registry saves it once, beside this. */
|
|
1247
1275
|
registry?: EngineSave;
|
|
@@ -1252,17 +1280,18 @@ interface SaveGame {
|
|
|
1252
1280
|
/** Each live flow's snapshot, keyed by flow id. */
|
|
1253
1281
|
flows: Record<string, FlowSnapshot>;
|
|
1254
1282
|
}
|
|
1255
|
-
/**
|
|
1256
|
-
* `
|
|
1283
|
+
/** A version 3 save, still read: the same shape apart from the cursor (`beatIndex` for `beatId`, no
|
|
1284
|
+
* `choiceId` on its frames) and a pending choice's options (each with its prompt text, discarded). */
|
|
1285
|
+
interface SaveGameV3 extends Omit<SaveGame, "version"> {
|
|
1286
|
+
version: 3;
|
|
1287
|
+
}
|
|
1288
|
+
/** One flow's own property values, without its id: what a `FlowState` carries beside its snapshot. */
|
|
1257
1289
|
interface FlowValues {
|
|
1258
1290
|
/** The NOT-shared `@patter` globals, under token "patter". */
|
|
1259
1291
|
scopes: EngineSave;
|
|
1260
1292
|
/** Per-scene NOT-shared `@scene` bags (scene id -> name -> value). */
|
|
1261
1293
|
sceneBags: Record<string, Record<string, ScalarValue>>;
|
|
1262
1294
|
}
|
|
1263
|
-
/** A version 2 flow snapshot, which carried the flow's property values itself. Still read. */
|
|
1264
|
-
interface FlowSnapshotV2 extends FlowSnapshot, FlowValues {
|
|
1265
|
-
}
|
|
1266
1295
|
/**
|
|
1267
1296
|
* One flow taken out of an engine (`engine.captureFlow`) to be put back later (`engine.restoreFlow`), under the
|
|
1268
1297
|
* same id or another: its snapshot as a save writes it, and its own property values without its id. Plain data
|
|
@@ -1270,19 +1299,8 @@ interface FlowSnapshotV2 extends FlowSnapshot, FlowValues {
|
|
|
1270
1299
|
* visit counts, shared selectors) is never in it, so restoring one rewinds nothing else.
|
|
1271
1300
|
*/
|
|
1272
1301
|
interface FlowState extends FlowSnapshot, FlowValues {
|
|
1273
|
-
version
|
|
1274
|
-
|
|
1275
|
-
/** A version 2 save, from before the registry held the properties. Every runtime still reads it: its
|
|
1276
|
-
* property sections move into the registry (under the keys in the header above) as it loads. */
|
|
1277
|
-
interface SaveGameV2 {
|
|
1278
|
-
version: 2;
|
|
1279
|
-
/** Shared `@patter` globals (owned scope "patter"). */
|
|
1280
|
-
shared: EngineSave;
|
|
1281
|
-
sharedVisits: Record<string, number>;
|
|
1282
|
-
sharedSelectors: Record<string, SelectorSnapshot>;
|
|
1283
|
-
/** Shared, scene-namespaced `@scene` bags (scene id -> name -> value). */
|
|
1284
|
-
stageBags: Record<string, Record<string, ScalarValue>>;
|
|
1285
|
-
flows: Record<string, FlowSnapshotV2>;
|
|
1302
|
+
/** `SAVE_VERSION` when written; a version 3 state still restores. */
|
|
1303
|
+
version: typeof SAVE_VERSION | 3;
|
|
1286
1304
|
}
|
|
1287
1305
|
/** Where a project keeps its shards unless its `layout` says otherwise, relative to the project root. */
|
|
1288
1306
|
declare const DEFAULT_LAYOUT: {
|
|
@@ -1394,4 +1412,4 @@ interface ExtensionFieldDecl {
|
|
|
1394
1412
|
/** A node's extension values in a bundle: extension id -> field -> value, raw. */
|
|
1395
1413
|
type NodeExtensions = Record<string, Record<string, unknown>>;
|
|
1396
1414
|
|
|
1397
|
-
export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleExtension, 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, EXTENSIONS_DIR, EXTENSION_SCHEMA, EXTENSION_SHARD, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, type ExtensionFieldDecl, type ExtensionShard, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, type
|
|
1415
|
+
export { AUTHORING_SCHEMA, type AuthoringFile, type Beat, type Block, type Bundle, type BundleCastMember, type BundleExtension, 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, EXTENSIONS_DIR, EXTENSION_SCHEMA, EXTENSION_SHARD, type EditRecord, type Effect, type EngineSave, type EstimatingConfig, type Expression, type ExtensionFieldDecl, type ExtensionShard, FLOW_SCHEMA, type FlowCursor, type FlowFile, type FlowNode, type FlowSnapshot, 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, LINE_CHARS_PER_SECOND, LINE_DURATION_MAX, LINE_DURATION_MIN, type LineBeat, type LocaleFile, MANIFEST_EXTENSION, type NodeExtensions, PAD_AFTER_MAX, PAD_AFTER_MIN, PROJECT_EXTENSION_STEM, PROJECT_LOCALE_SCENE, PROJECT_SCHEMA, type ProjectDictionary, type ProjectExtension, type ProjectFile, type ProjectLayout, 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 SaveGameV3, 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, UNRESOLVED_KEY, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, estimateLineDuration, formatSpeakerLabel, isContentlessBeat, isDialogueScene, projectLayout, projectQualifiers, qualifierStringKey, resolveSceneRef, sceneAddressIndex, speakerLabelKey, textIsAction, usedQualifiers, usedSpeakerPairs, walkNodes };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import { gameIdify, isValidGameId } from "@wildwinter/toolkit";
|
|
2
3
|
import {
|
|
3
4
|
propertyNameify,
|
|
4
5
|
isValidPropertyName,
|
|
@@ -19,12 +20,6 @@ function isContentlessBeat(beat, hasText) {
|
|
|
19
20
|
if (beat.tags && beat.tags.length > 0) return false;
|
|
20
21
|
return beat.kind === "text" || !beat.character && !beat.direction;
|
|
21
22
|
}
|
|
22
|
-
function gameIdify(text) {
|
|
23
|
-
return text.toLowerCase().replace(/['’]/g, "").replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
24
|
-
}
|
|
25
|
-
function isValidGameId(gameId) {
|
|
26
|
-
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);
|
|
27
|
-
}
|
|
28
23
|
function effectiveGameId(entity) {
|
|
29
24
|
const g = entity.gameId?.trim();
|
|
30
25
|
return g ? g : gameIdify(entity.name);
|
|
@@ -108,6 +103,14 @@ function textIsAction(project, scene) {
|
|
|
108
103
|
var DEFAULT_PAD_AFTER = 0.6;
|
|
109
104
|
var PAD_AFTER_MIN = -10;
|
|
110
105
|
var PAD_AFTER_MAX = 60;
|
|
106
|
+
var LINE_CHARS_PER_SECOND = 14;
|
|
107
|
+
var LINE_DURATION_MIN = 1.5;
|
|
108
|
+
var LINE_DURATION_MAX = 18;
|
|
109
|
+
function estimateLineDuration(text, _locale) {
|
|
110
|
+
let count = 0;
|
|
111
|
+
for (const ch of text ?? "") if (ch !== "\xAD") count++;
|
|
112
|
+
return Math.min(Math.max(count / LINE_CHARS_PER_SECOND, LINE_DURATION_MIN), LINE_DURATION_MAX);
|
|
113
|
+
}
|
|
111
114
|
var DEFAULT_WRITING_STATUSES = [
|
|
112
115
|
{ name: "stub", colour: 0 },
|
|
113
116
|
// red
|
|
@@ -168,7 +171,7 @@ var HANDOFF_SCHEMA = "patter/handoff@0";
|
|
|
168
171
|
var DEFAULT_CAPTION_DELIMITERS = { open: "[", close: "]" };
|
|
169
172
|
var DEFAULT_CAPTION_CHARACTER = "SFX";
|
|
170
173
|
var SAVE_SCHEMA = "patter/save@0";
|
|
171
|
-
var SAVE_VERSION =
|
|
174
|
+
var SAVE_VERSION = 4;
|
|
172
175
|
var DEFAULT_LAYOUT = { flow: "scenes/", strings: "loc/", authoring: "authoring/", extensions: "extensions/" };
|
|
173
176
|
function projectLayout(project) {
|
|
174
177
|
return { ...DEFAULT_LAYOUT, ...project.layout };
|
|
@@ -200,6 +203,9 @@ export {
|
|
|
200
203
|
EXTENSION_SHARD,
|
|
201
204
|
FLOW_SCHEMA,
|
|
202
205
|
HANDOFF_SCHEMA,
|
|
206
|
+
LINE_CHARS_PER_SECOND,
|
|
207
|
+
LINE_DURATION_MAX,
|
|
208
|
+
LINE_DURATION_MIN,
|
|
203
209
|
MANIFEST_EXTENSION,
|
|
204
210
|
PAD_AFTER_MAX,
|
|
205
211
|
PAD_AFTER_MIN,
|
|
@@ -217,6 +223,7 @@ export {
|
|
|
217
223
|
castStringKey,
|
|
218
224
|
deriveRecordingFolders,
|
|
219
225
|
effectiveGameId,
|
|
226
|
+
estimateLineDuration,
|
|
220
227
|
formatSpeakerLabel,
|
|
221
228
|
gameIdify,
|
|
222
229
|
isCaseOnlyPropertyName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/model",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@wildwinter/expr": "^0.6.0"
|
|
37
|
+
"@wildwinter/expr": "^0.6.0",
|
|
38
|
+
"@wildwinter/toolkit": "^0.4.0"
|
|
38
39
|
}
|
|
39
40
|
}
|