@patterkit/model 0.21.0 → 0.23.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 +74 -0
- package/dist/index.d.cts +61 -2
- package/dist/index.d.ts +61 -2
- package/dist/index.js +68 -0
- package/package.json +1 -1
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,6 +56,7 @@ __export(index_exports, {
|
|
|
53
56
|
castStringKey: () => castStringKey,
|
|
54
57
|
deriveRecordingFolders: () => deriveRecordingFolders,
|
|
55
58
|
effectiveGameId: () => effectiveGameId,
|
|
59
|
+
estimateLineDuration: () => estimateLineDuration,
|
|
56
60
|
formatSpeakerLabel: () => formatSpeakerLabel,
|
|
57
61
|
gameIdify: () => import_toolkit.gameIdify,
|
|
58
62
|
isCaseOnlyPropertyName: () => import_expr.isCaseOnlyPropertyName,
|
|
@@ -66,6 +70,8 @@ __export(index_exports, {
|
|
|
66
70
|
qualifierStringKey: () => qualifierStringKey,
|
|
67
71
|
resolveSceneRef: () => resolveSceneRef,
|
|
68
72
|
sceneAddressIndex: () => sceneAddressIndex,
|
|
73
|
+
sceneFolderLayout: () => sceneFolderLayout,
|
|
74
|
+
sceneFolderPath: () => sceneFolderPath,
|
|
69
75
|
speakerLabelKey: () => speakerLabelKey,
|
|
70
76
|
textIsAction: () => textIsAction,
|
|
71
77
|
usedQualifiers: () => usedQualifiers,
|
|
@@ -172,6 +178,14 @@ function textIsAction(project, scene) {
|
|
|
172
178
|
var DEFAULT_PAD_AFTER = 0.6;
|
|
173
179
|
var PAD_AFTER_MIN = -10;
|
|
174
180
|
var PAD_AFTER_MAX = 60;
|
|
181
|
+
var LINE_CHARS_PER_SECOND = 14;
|
|
182
|
+
var LINE_DURATION_MIN = 1.5;
|
|
183
|
+
var LINE_DURATION_MAX = 18;
|
|
184
|
+
function estimateLineDuration(text, _locale) {
|
|
185
|
+
let count = 0;
|
|
186
|
+
for (const ch of text ?? "") if (ch !== "\xAD") count++;
|
|
187
|
+
return Math.min(Math.max(count / LINE_CHARS_PER_SECOND, LINE_DURATION_MIN), LINE_DURATION_MAX);
|
|
188
|
+
}
|
|
175
189
|
var DEFAULT_WRITING_STATUSES = [
|
|
176
190
|
{ name: "stub", colour: 0 },
|
|
177
191
|
// red
|
|
@@ -213,6 +227,60 @@ var DEFAULT_DOCUMENTATION_CLASSES = [
|
|
|
213
227
|
{ name: "editor", deliver: ["editor"] }
|
|
214
228
|
// the editable script handed to an outside editor
|
|
215
229
|
];
|
|
230
|
+
function sceneFolderLayout(project, sceneIds) {
|
|
231
|
+
const folders = project.sceneFolders ?? [];
|
|
232
|
+
const known = new Set(folders.map((f) => f.id));
|
|
233
|
+
const folderParent = /* @__PURE__ */ new Map();
|
|
234
|
+
for (const f of folders) folderParent.set(f.id, f.parent !== void 0 && known.has(f.parent) && f.parent !== f.id ? f.parent : void 0);
|
|
235
|
+
for (const f of folders) {
|
|
236
|
+
let at = folderParent.get(f.id);
|
|
237
|
+
for (let steps = 0; at !== void 0 && steps <= folders.length; steps++) {
|
|
238
|
+
if (at === f.id) {
|
|
239
|
+
folderParent.set(f.id, void 0);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
at = folderParent.get(at);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const sceneFolder = /* @__PURE__ */ new Map();
|
|
246
|
+
for (const id of sceneIds) {
|
|
247
|
+
const f = project.sceneFolder?.[id];
|
|
248
|
+
sceneFolder.set(id, f !== void 0 && known.has(f) ? f : void 0);
|
|
249
|
+
}
|
|
250
|
+
const rank = new Map((project.sceneOrder ?? []).map((id, i) => [id, i]));
|
|
251
|
+
const children = /* @__PURE__ */ new Map();
|
|
252
|
+
const add = (parent, kind, id, tie) => {
|
|
253
|
+
const list = children.get(parent) ?? [];
|
|
254
|
+
list.push({ kind, id, tie });
|
|
255
|
+
children.set(parent, list);
|
|
256
|
+
};
|
|
257
|
+
sceneIds.forEach((id, i) => add(sceneFolder.get(id), "scene", id, i));
|
|
258
|
+
folders.forEach((f, i) => add(folderParent.get(f.id), "folder", f.id, sceneIds.length + i));
|
|
259
|
+
const rows = [];
|
|
260
|
+
const out = [];
|
|
261
|
+
const walk = (parent, depth) => {
|
|
262
|
+
const list = (children.get(parent) ?? []).slice().sort((a, b) => {
|
|
263
|
+
const ra = rank.get(a.id), rb = rank.get(b.id);
|
|
264
|
+
if (ra !== void 0 && rb !== void 0) return ra - rb;
|
|
265
|
+
if (ra !== void 0) return -1;
|
|
266
|
+
if (rb !== void 0) return 1;
|
|
267
|
+
return a.tie - b.tie;
|
|
268
|
+
});
|
|
269
|
+
for (const c of list) {
|
|
270
|
+
rows.push({ kind: c.kind, id: c.id, depth, ...parent !== void 0 ? { folder: parent } : {} });
|
|
271
|
+
if (c.kind === "scene") out.push(c.id);
|
|
272
|
+
else walk(c.id, depth + 1);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
walk(void 0, 0);
|
|
276
|
+
return { rows, sceneIds: out, folderParent, sceneFolder };
|
|
277
|
+
}
|
|
278
|
+
function sceneFolderPath(project, layout, sceneId) {
|
|
279
|
+
const names = new Map((project.sceneFolders ?? []).map((f) => [f.id, f.name]));
|
|
280
|
+
const parts = [];
|
|
281
|
+
for (let at = layout.sceneFolder.get(sceneId), n = 0; at !== void 0 && n <= names.size; at = layout.folderParent.get(at), n++) parts.unshift(names.get(at) ?? "");
|
|
282
|
+
return parts.join(" / ");
|
|
283
|
+
}
|
|
216
284
|
var PROJECT_LOCALE_SCENE = "@project";
|
|
217
285
|
function castStringKey(name) {
|
|
218
286
|
return `cast:${name}`;
|
|
@@ -265,6 +333,9 @@ var UNRESOLVED_KEY = "unresolved";
|
|
|
265
333
|
EXTENSION_SHARD,
|
|
266
334
|
FLOW_SCHEMA,
|
|
267
335
|
HANDOFF_SCHEMA,
|
|
336
|
+
LINE_CHARS_PER_SECOND,
|
|
337
|
+
LINE_DURATION_MAX,
|
|
338
|
+
LINE_DURATION_MIN,
|
|
268
339
|
MANIFEST_EXTENSION,
|
|
269
340
|
PAD_AFTER_MAX,
|
|
270
341
|
PAD_AFTER_MIN,
|
|
@@ -282,6 +353,7 @@ var UNRESOLVED_KEY = "unresolved";
|
|
|
282
353
|
castStringKey,
|
|
283
354
|
deriveRecordingFolders,
|
|
284
355
|
effectiveGameId,
|
|
356
|
+
estimateLineDuration,
|
|
285
357
|
formatSpeakerLabel,
|
|
286
358
|
gameIdify,
|
|
287
359
|
isCaseOnlyPropertyName,
|
|
@@ -295,6 +367,8 @@ var UNRESOLVED_KEY = "unresolved";
|
|
|
295
367
|
qualifierStringKey,
|
|
296
368
|
resolveSceneRef,
|
|
297
369
|
sceneAddressIndex,
|
|
370
|
+
sceneFolderLayout,
|
|
371
|
+
sceneFolderPath,
|
|
298
372
|
speakerLabelKey,
|
|
299
373
|
textIsAction,
|
|
300
374
|
usedQualifiers,
|
package/dist/index.d.cts
CHANGED
|
@@ -431,6 +431,17 @@ declare const DEFAULT_PAD_AFTER = 0.6;
|
|
|
431
431
|
/** The range validation keeps a `padAfter` or `padAfterDefault` within, in seconds. */
|
|
432
432
|
declare const PAD_AFTER_MIN = -10;
|
|
433
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;
|
|
434
445
|
/** A qualifier as it reaches a compiled bundle: its `gameId` and authored name (the unlocalised fallback). */
|
|
435
446
|
interface BundleQualifier {
|
|
436
447
|
gameId: string;
|
|
@@ -588,6 +599,43 @@ interface EstimatingConfig {
|
|
|
588
599
|
lines: number;
|
|
589
600
|
}[];
|
|
590
601
|
}
|
|
602
|
+
/** A folder in the scene list (ProjectFile.sceneFolders): organisational only, it carries no meaning. */
|
|
603
|
+
interface SceneFolder {
|
|
604
|
+
id: string;
|
|
605
|
+
name: string;
|
|
606
|
+
/** The folder it sits in; omitted = the top level. A parent that doesn't exist, or a loop of parents
|
|
607
|
+
* (only a bad merge makes one), reads as the top level. */
|
|
608
|
+
parent?: string;
|
|
609
|
+
}
|
|
610
|
+
/** One row of the scene list as laid out by `sceneFolderLayout`, top to bottom with every folder open. */
|
|
611
|
+
interface SceneNavRow {
|
|
612
|
+
kind: "folder" | "scene";
|
|
613
|
+
id: string;
|
|
614
|
+
/** 0 = the top level. */
|
|
615
|
+
depth: number;
|
|
616
|
+
/** The folder this row sits in, if any. */
|
|
617
|
+
folder?: string;
|
|
618
|
+
}
|
|
619
|
+
interface SceneFolderLayout {
|
|
620
|
+
/** Every folder and scene, depth first, in authored order. */
|
|
621
|
+
rows: SceneNavRow[];
|
|
622
|
+
/** The scene ids in that order: what the nav, the exports, and the bundle list scenes by. */
|
|
623
|
+
sceneIds: string[];
|
|
624
|
+
/** Each folder's effective parent (undefined = top level), after the forgiving rules. */
|
|
625
|
+
folderParent: Map<string, string | undefined>;
|
|
626
|
+
/** Each scene's effective folder (undefined = top level). */
|
|
627
|
+
sceneFolder: Map<string, string | undefined>;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Lay out the scene list: folders and scenes, nested and ordered. Forgiving, as `sceneOrder` always was:
|
|
631
|
+
* a scene filed in a missing folder, or a folder with a missing parent, sits at the top level; a loop of
|
|
632
|
+
* parents is broken at the first folder of it in `sceneFolders`; ids in `sceneOrder` that are neither
|
|
633
|
+
* are ignored. In each level the rows `sceneOrder` lists come first, in its order, then unlisted scenes in
|
|
634
|
+
* the order given, then unlisted folders in `sceneFolders` order.
|
|
635
|
+
*/
|
|
636
|
+
declare function sceneFolderLayout(project: Pick<ProjectFile, "sceneOrder" | "sceneFolders" | "sceneFolder">, sceneIds: readonly string[]): SceneFolderLayout;
|
|
637
|
+
/** A scene's folder path for display ("Act 1 / The docks"), or "" at the top level. */
|
|
638
|
+
declare function sceneFolderPath(project: Pick<ProjectFile, "sceneFolders">, layout: Pick<SceneFolderLayout, "folderParent" | "sceneFolder">, sceneId: string): string;
|
|
591
639
|
interface ProjectFile {
|
|
592
640
|
schema: string;
|
|
593
641
|
root?: boolean;
|
|
@@ -608,8 +656,17 @@ interface ProjectFile {
|
|
|
608
656
|
};
|
|
609
657
|
/** The authored scene order (scene ids) for navigation. Scenes not listed follow in file order;
|
|
610
658
|
* listed ids that no longer exist are ignored. Omitted = plain file order. Presentation only:
|
|
611
|
-
* it never affects play, which always starts from `start` / an explicit address.
|
|
659
|
+
* it never affects play, which always starts from `start` / an explicit address.
|
|
660
|
+
* Holds scene folder ids too (`sceneFolders`): folders and scenes mix in one order, and Patterpad keeps
|
|
661
|
+
* the list equal to the scene list as shown, read top to bottom with every folder open. */
|
|
612
662
|
sceneOrder?: string[];
|
|
663
|
+
/** Folders for organising the scene list (design/proposals/scene-folders.md). Navigation only, like
|
|
664
|
+
* `sceneOrder`: no file moves, nothing reaches the bundle. The list's own order means nothing; where a
|
|
665
|
+
* folder is shown comes from `sceneOrder`. Omitted = no folders. */
|
|
666
|
+
sceneFolders?: SceneFolder[];
|
|
667
|
+
/** Scene id -> the id of the folder it's filed in. A scene not listed sits at the top level; an entry
|
|
668
|
+
* naming a folder that doesn't exist is ignored. A map, so two people filing different scenes merge. */
|
|
669
|
+
sceneFolder?: Record<string, string>;
|
|
613
670
|
/** Version-control system (spec §12); omitted = unset / none. */
|
|
614
671
|
vcs?: VcsKind;
|
|
615
672
|
/** Project-wide VO mode (spec §16) - one boolean, no per-scene override. */
|
|
@@ -1224,6 +1281,8 @@ interface SavedChoice {
|
|
|
1224
1281
|
/** The flow's execution position: where it is, and what it is waiting on. */
|
|
1225
1282
|
interface FlowCursor {
|
|
1226
1283
|
flowEnded: boolean;
|
|
1284
|
+
/** The choice group whose running dry ended the flow (its `end` step's reason "ran-dry"). Present only then. */
|
|
1285
|
+
ranDry?: string;
|
|
1227
1286
|
currentSceneId: string | null;
|
|
1228
1287
|
/** The continuation stack (call frames + the active block run), root first. */
|
|
1229
1288
|
stack: StackFrame[];
|
|
@@ -1401,4 +1460,4 @@ interface ExtensionFieldDecl {
|
|
|
1401
1460
|
/** A node's extension values in a bundle: extension id -> field -> value, raw. */
|
|
1402
1461
|
type NodeExtensions = Record<string, Record<string, unknown>>;
|
|
1403
1462
|
|
|
1404
|
-
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, 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, formatSpeakerLabel, isContentlessBeat, isDialogueScene, projectLayout, projectQualifiers, qualifierStringKey, resolveSceneRef, sceneAddressIndex, speakerLabelKey, textIsAction, usedQualifiers, usedSpeakerPairs, walkNodes };
|
|
1463
|
+
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 SceneFolder, type SceneFolderLayout, type SceneNavRow, 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, sceneFolderLayout, sceneFolderPath, speakerLabelKey, textIsAction, usedQualifiers, usedSpeakerPairs, walkNodes };
|
package/dist/index.d.ts
CHANGED
|
@@ -431,6 +431,17 @@ declare const DEFAULT_PAD_AFTER = 0.6;
|
|
|
431
431
|
/** The range validation keeps a `padAfter` or `padAfterDefault` within, in seconds. */
|
|
432
432
|
declare const PAD_AFTER_MIN = -10;
|
|
433
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;
|
|
434
445
|
/** A qualifier as it reaches a compiled bundle: its `gameId` and authored name (the unlocalised fallback). */
|
|
435
446
|
interface BundleQualifier {
|
|
436
447
|
gameId: string;
|
|
@@ -588,6 +599,43 @@ interface EstimatingConfig {
|
|
|
588
599
|
lines: number;
|
|
589
600
|
}[];
|
|
590
601
|
}
|
|
602
|
+
/** A folder in the scene list (ProjectFile.sceneFolders): organisational only, it carries no meaning. */
|
|
603
|
+
interface SceneFolder {
|
|
604
|
+
id: string;
|
|
605
|
+
name: string;
|
|
606
|
+
/** The folder it sits in; omitted = the top level. A parent that doesn't exist, or a loop of parents
|
|
607
|
+
* (only a bad merge makes one), reads as the top level. */
|
|
608
|
+
parent?: string;
|
|
609
|
+
}
|
|
610
|
+
/** One row of the scene list as laid out by `sceneFolderLayout`, top to bottom with every folder open. */
|
|
611
|
+
interface SceneNavRow {
|
|
612
|
+
kind: "folder" | "scene";
|
|
613
|
+
id: string;
|
|
614
|
+
/** 0 = the top level. */
|
|
615
|
+
depth: number;
|
|
616
|
+
/** The folder this row sits in, if any. */
|
|
617
|
+
folder?: string;
|
|
618
|
+
}
|
|
619
|
+
interface SceneFolderLayout {
|
|
620
|
+
/** Every folder and scene, depth first, in authored order. */
|
|
621
|
+
rows: SceneNavRow[];
|
|
622
|
+
/** The scene ids in that order: what the nav, the exports, and the bundle list scenes by. */
|
|
623
|
+
sceneIds: string[];
|
|
624
|
+
/** Each folder's effective parent (undefined = top level), after the forgiving rules. */
|
|
625
|
+
folderParent: Map<string, string | undefined>;
|
|
626
|
+
/** Each scene's effective folder (undefined = top level). */
|
|
627
|
+
sceneFolder: Map<string, string | undefined>;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Lay out the scene list: folders and scenes, nested and ordered. Forgiving, as `sceneOrder` always was:
|
|
631
|
+
* a scene filed in a missing folder, or a folder with a missing parent, sits at the top level; a loop of
|
|
632
|
+
* parents is broken at the first folder of it in `sceneFolders`; ids in `sceneOrder` that are neither
|
|
633
|
+
* are ignored. In each level the rows `sceneOrder` lists come first, in its order, then unlisted scenes in
|
|
634
|
+
* the order given, then unlisted folders in `sceneFolders` order.
|
|
635
|
+
*/
|
|
636
|
+
declare function sceneFolderLayout(project: Pick<ProjectFile, "sceneOrder" | "sceneFolders" | "sceneFolder">, sceneIds: readonly string[]): SceneFolderLayout;
|
|
637
|
+
/** A scene's folder path for display ("Act 1 / The docks"), or "" at the top level. */
|
|
638
|
+
declare function sceneFolderPath(project: Pick<ProjectFile, "sceneFolders">, layout: Pick<SceneFolderLayout, "folderParent" | "sceneFolder">, sceneId: string): string;
|
|
591
639
|
interface ProjectFile {
|
|
592
640
|
schema: string;
|
|
593
641
|
root?: boolean;
|
|
@@ -608,8 +656,17 @@ interface ProjectFile {
|
|
|
608
656
|
};
|
|
609
657
|
/** The authored scene order (scene ids) for navigation. Scenes not listed follow in file order;
|
|
610
658
|
* listed ids that no longer exist are ignored. Omitted = plain file order. Presentation only:
|
|
611
|
-
* it never affects play, which always starts from `start` / an explicit address.
|
|
659
|
+
* it never affects play, which always starts from `start` / an explicit address.
|
|
660
|
+
* Holds scene folder ids too (`sceneFolders`): folders and scenes mix in one order, and Patterpad keeps
|
|
661
|
+
* the list equal to the scene list as shown, read top to bottom with every folder open. */
|
|
612
662
|
sceneOrder?: string[];
|
|
663
|
+
/** Folders for organising the scene list (design/proposals/scene-folders.md). Navigation only, like
|
|
664
|
+
* `sceneOrder`: no file moves, nothing reaches the bundle. The list's own order means nothing; where a
|
|
665
|
+
* folder is shown comes from `sceneOrder`. Omitted = no folders. */
|
|
666
|
+
sceneFolders?: SceneFolder[];
|
|
667
|
+
/** Scene id -> the id of the folder it's filed in. A scene not listed sits at the top level; an entry
|
|
668
|
+
* naming a folder that doesn't exist is ignored. A map, so two people filing different scenes merge. */
|
|
669
|
+
sceneFolder?: Record<string, string>;
|
|
613
670
|
/** Version-control system (spec §12); omitted = unset / none. */
|
|
614
671
|
vcs?: VcsKind;
|
|
615
672
|
/** Project-wide VO mode (spec §16) - one boolean, no per-scene override. */
|
|
@@ -1224,6 +1281,8 @@ interface SavedChoice {
|
|
|
1224
1281
|
/** The flow's execution position: where it is, and what it is waiting on. */
|
|
1225
1282
|
interface FlowCursor {
|
|
1226
1283
|
flowEnded: boolean;
|
|
1284
|
+
/** The choice group whose running dry ended the flow (its `end` step's reason "ran-dry"). Present only then. */
|
|
1285
|
+
ranDry?: string;
|
|
1227
1286
|
currentSceneId: string | null;
|
|
1228
1287
|
/** The continuation stack (call frames + the active block run), root first. */
|
|
1229
1288
|
stack: StackFrame[];
|
|
@@ -1401,4 +1460,4 @@ interface ExtensionFieldDecl {
|
|
|
1401
1460
|
/** A node's extension values in a bundle: extension id -> field -> value, raw. */
|
|
1402
1461
|
type NodeExtensions = Record<string, Record<string, unknown>>;
|
|
1403
1462
|
|
|
1404
|
-
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, 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, formatSpeakerLabel, isContentlessBeat, isDialogueScene, projectLayout, projectQualifiers, qualifierStringKey, resolveSceneRef, sceneAddressIndex, speakerLabelKey, textIsAction, usedQualifiers, usedSpeakerPairs, walkNodes };
|
|
1463
|
+
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 SceneFolder, type SceneFolderLayout, type SceneNavRow, 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, sceneFolderLayout, sceneFolderPath, speakerLabelKey, textIsAction, usedQualifiers, usedSpeakerPairs, walkNodes };
|
package/dist/index.js
CHANGED
|
@@ -103,6 +103,14 @@ function textIsAction(project, scene) {
|
|
|
103
103
|
var DEFAULT_PAD_AFTER = 0.6;
|
|
104
104
|
var PAD_AFTER_MIN = -10;
|
|
105
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
|
+
}
|
|
106
114
|
var DEFAULT_WRITING_STATUSES = [
|
|
107
115
|
{ name: "stub", colour: 0 },
|
|
108
116
|
// red
|
|
@@ -144,6 +152,60 @@ var DEFAULT_DOCUMENTATION_CLASSES = [
|
|
|
144
152
|
{ name: "editor", deliver: ["editor"] }
|
|
145
153
|
// the editable script handed to an outside editor
|
|
146
154
|
];
|
|
155
|
+
function sceneFolderLayout(project, sceneIds) {
|
|
156
|
+
const folders = project.sceneFolders ?? [];
|
|
157
|
+
const known = new Set(folders.map((f) => f.id));
|
|
158
|
+
const folderParent = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const f of folders) folderParent.set(f.id, f.parent !== void 0 && known.has(f.parent) && f.parent !== f.id ? f.parent : void 0);
|
|
160
|
+
for (const f of folders) {
|
|
161
|
+
let at = folderParent.get(f.id);
|
|
162
|
+
for (let steps = 0; at !== void 0 && steps <= folders.length; steps++) {
|
|
163
|
+
if (at === f.id) {
|
|
164
|
+
folderParent.set(f.id, void 0);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
at = folderParent.get(at);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const sceneFolder = /* @__PURE__ */ new Map();
|
|
171
|
+
for (const id of sceneIds) {
|
|
172
|
+
const f = project.sceneFolder?.[id];
|
|
173
|
+
sceneFolder.set(id, f !== void 0 && known.has(f) ? f : void 0);
|
|
174
|
+
}
|
|
175
|
+
const rank = new Map((project.sceneOrder ?? []).map((id, i) => [id, i]));
|
|
176
|
+
const children = /* @__PURE__ */ new Map();
|
|
177
|
+
const add = (parent, kind, id, tie) => {
|
|
178
|
+
const list = children.get(parent) ?? [];
|
|
179
|
+
list.push({ kind, id, tie });
|
|
180
|
+
children.set(parent, list);
|
|
181
|
+
};
|
|
182
|
+
sceneIds.forEach((id, i) => add(sceneFolder.get(id), "scene", id, i));
|
|
183
|
+
folders.forEach((f, i) => add(folderParent.get(f.id), "folder", f.id, sceneIds.length + i));
|
|
184
|
+
const rows = [];
|
|
185
|
+
const out = [];
|
|
186
|
+
const walk = (parent, depth) => {
|
|
187
|
+
const list = (children.get(parent) ?? []).slice().sort((a, b) => {
|
|
188
|
+
const ra = rank.get(a.id), rb = rank.get(b.id);
|
|
189
|
+
if (ra !== void 0 && rb !== void 0) return ra - rb;
|
|
190
|
+
if (ra !== void 0) return -1;
|
|
191
|
+
if (rb !== void 0) return 1;
|
|
192
|
+
return a.tie - b.tie;
|
|
193
|
+
});
|
|
194
|
+
for (const c of list) {
|
|
195
|
+
rows.push({ kind: c.kind, id: c.id, depth, ...parent !== void 0 ? { folder: parent } : {} });
|
|
196
|
+
if (c.kind === "scene") out.push(c.id);
|
|
197
|
+
else walk(c.id, depth + 1);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
walk(void 0, 0);
|
|
201
|
+
return { rows, sceneIds: out, folderParent, sceneFolder };
|
|
202
|
+
}
|
|
203
|
+
function sceneFolderPath(project, layout, sceneId) {
|
|
204
|
+
const names = new Map((project.sceneFolders ?? []).map((f) => [f.id, f.name]));
|
|
205
|
+
const parts = [];
|
|
206
|
+
for (let at = layout.sceneFolder.get(sceneId), n = 0; at !== void 0 && n <= names.size; at = layout.folderParent.get(at), n++) parts.unshift(names.get(at) ?? "");
|
|
207
|
+
return parts.join(" / ");
|
|
208
|
+
}
|
|
147
209
|
var PROJECT_LOCALE_SCENE = "@project";
|
|
148
210
|
function castStringKey(name) {
|
|
149
211
|
return `cast:${name}`;
|
|
@@ -195,6 +257,9 @@ export {
|
|
|
195
257
|
EXTENSION_SHARD,
|
|
196
258
|
FLOW_SCHEMA,
|
|
197
259
|
HANDOFF_SCHEMA,
|
|
260
|
+
LINE_CHARS_PER_SECOND,
|
|
261
|
+
LINE_DURATION_MAX,
|
|
262
|
+
LINE_DURATION_MIN,
|
|
198
263
|
MANIFEST_EXTENSION,
|
|
199
264
|
PAD_AFTER_MAX,
|
|
200
265
|
PAD_AFTER_MIN,
|
|
@@ -212,6 +277,7 @@ export {
|
|
|
212
277
|
castStringKey,
|
|
213
278
|
deriveRecordingFolders,
|
|
214
279
|
effectiveGameId,
|
|
280
|
+
estimateLineDuration,
|
|
215
281
|
formatSpeakerLabel,
|
|
216
282
|
gameIdify,
|
|
217
283
|
isCaseOnlyPropertyName,
|
|
@@ -225,6 +291,8 @@ export {
|
|
|
225
291
|
qualifierStringKey,
|
|
226
292
|
resolveSceneRef,
|
|
227
293
|
sceneAddressIndex,
|
|
294
|
+
sceneFolderLayout,
|
|
295
|
+
sceneFolderPath,
|
|
228
296
|
speakerLabelKey,
|
|
229
297
|
textIsAction,
|
|
230
298
|
usedQualifiers,
|
package/package.json
CHANGED