@patterkit/model 0.1.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 +120 -0
- package/dist/index.d.cts +782 -0
- package/dist/index.d.ts +782 -0
- package/dist/index.js +83 -0
- package/package.json +30 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
import { AstNode } from '@wildwinter/expr';
|
|
2
|
+
|
|
3
|
+
type ScalarValue = boolean | number | string | string[];
|
|
4
|
+
/** Developer-defined host metadata (spec §17). Opaque to Patter. */
|
|
5
|
+
type GameData = Record<string, unknown>;
|
|
6
|
+
type Effect = {
|
|
7
|
+
kind: "set";
|
|
8
|
+
target: string;
|
|
9
|
+
value: string;
|
|
10
|
+
};
|
|
11
|
+
/** A scene/block id, or the reserved "END". */
|
|
12
|
+
type JumpTarget = string;
|
|
13
|
+
interface Jump {
|
|
14
|
+
to: JumpTarget;
|
|
15
|
+
/** "jump" (one-way, default) or "call" (jump-and-return via the flow callstack). */
|
|
16
|
+
mode?: "jump" | "call";
|
|
17
|
+
}
|
|
18
|
+
interface LineBeat {
|
|
19
|
+
id: string;
|
|
20
|
+
kind: "line";
|
|
21
|
+
/** Speaker; must be a member of the project cast (validated). */
|
|
22
|
+
character?: string;
|
|
23
|
+
/** Performance direction, language-neutral (never localised). */
|
|
24
|
+
direction?: string;
|
|
25
|
+
gameData?: GameData;
|
|
26
|
+
/** Author-defined freeform tags (#215): a cross-cutting label layer that travels to the runtime.
|
|
27
|
+
* At runtime a beat's tags are the UNION of its own and every ancestor's (scene → block → group(s) →
|
|
28
|
+
* snippet → beat). Each tag is letters/digits/symbols with NO comma and NO whitespace; deduped. */
|
|
29
|
+
tags?: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Authorial voice / narration - speaker-less prose (spec §2). Never voiced; its
|
|
33
|
+
* localised text always permits inline `{@name}` interpolation (spec §16). This
|
|
34
|
+
* is the on-screen-text role (e.g. "A door slams!") - distinct from a game-event
|
|
35
|
+
* beat, which is a pure engine instruction with no localised content.
|
|
36
|
+
*/
|
|
37
|
+
interface TextBeat {
|
|
38
|
+
id: string;
|
|
39
|
+
kind: "text";
|
|
40
|
+
gameData?: GameData;
|
|
41
|
+
/** Author tags (#215). See LineBeat.tags. */
|
|
42
|
+
tags?: string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A GAME EVENT (spec §2): a pure engine instruction with no player-facing text - just `gameData` the host
|
|
46
|
+
* reads when the beat plays (comments / docs attach via the authoring file by id). Named "game event"
|
|
47
|
+
* rather than "action" because a screenplay's "action" is prose, which is our text beat. Player-facing
|
|
48
|
+
* words are a line or text beat instead.
|
|
49
|
+
*/
|
|
50
|
+
interface GameEventBeat {
|
|
51
|
+
id: string;
|
|
52
|
+
kind: "gameEvent";
|
|
53
|
+
gameData?: GameData;
|
|
54
|
+
/** Author tags (#215). See LineBeat.tags. */
|
|
55
|
+
tags?: string[];
|
|
56
|
+
}
|
|
57
|
+
type Beat = LineBeat | TextBeat | GameEventBeat;
|
|
58
|
+
type Selector = "run" | "branch" | "sequence" | "choice";
|
|
59
|
+
/** How a `sequence` walks its children. */
|
|
60
|
+
type SelectorOrder = "sequential" | "shuffle";
|
|
61
|
+
/** What a `sequence` does after one full pass through its children. */
|
|
62
|
+
type SelectorExhaust = "once" | "repeat" | "stick";
|
|
63
|
+
/**
|
|
64
|
+
* `sequence` selector config (spec §4). One stateful picker with two orthogonal
|
|
65
|
+
* axes subsumes Ink's stopping / cycle / once / shuffle and their combinations.
|
|
66
|
+
* Defaults: `order: "sequential"`, `exhaust: "once"`. `shuffle` draws without
|
|
67
|
+
* replacement and never repeats a line back-to-back (built in).
|
|
68
|
+
*/
|
|
69
|
+
interface SequenceOptions {
|
|
70
|
+
order?: SelectorOrder;
|
|
71
|
+
exhaust?: SelectorExhaust;
|
|
72
|
+
}
|
|
73
|
+
interface Snippet {
|
|
74
|
+
id: string;
|
|
75
|
+
type: "snippet";
|
|
76
|
+
/** Eligibility (and the conditional-jump test). Expression src. */
|
|
77
|
+
condition?: string;
|
|
78
|
+
/** Zero or more beats; zero beats + a jump = a pure jump (spec §3). */
|
|
79
|
+
beats?: Beat[];
|
|
80
|
+
onEnter?: Effect[];
|
|
81
|
+
onExit?: Effect[];
|
|
82
|
+
gameData?: GameData;
|
|
83
|
+
/** Author tags (#215). See LineBeat.tags. */
|
|
84
|
+
tags?: string[];
|
|
85
|
+
/** Optional routing action; fires after `beats`. */
|
|
86
|
+
jump?: Jump;
|
|
87
|
+
/** Omit this option entirely when its condition fails (default: show greyed). */
|
|
88
|
+
secretUntilEligible?: boolean;
|
|
89
|
+
/** Repeatable choice option (spec §5). `true`: always offered while its condition passes (Ink `+`).
|
|
90
|
+
* Default `false`: once-only - after the player follows it once it is gone from `getChoices`
|
|
91
|
+
* entirely (not delivered, not flagged unavailable - just absent; Ink `*`). */
|
|
92
|
+
sticky?: boolean;
|
|
93
|
+
/** The choice's fallback option (spec §5). Never delivered as a normal option; auto-followed the
|
|
94
|
+
* moment it is the ONLY eligible option left (its own condition still applies). At most one per
|
|
95
|
+
* choice. Default `false`. */
|
|
96
|
+
fallback?: boolean;
|
|
97
|
+
}
|
|
98
|
+
/** An option's prompt beat (spec §5): a single line | text beat - the choice text. */
|
|
99
|
+
type PromptBeat = LineBeat | TextBeat;
|
|
100
|
+
interface Group {
|
|
101
|
+
id: string;
|
|
102
|
+
type: "group";
|
|
103
|
+
condition?: string;
|
|
104
|
+
/** How the group's children are walked. Default (omitted) = `"run"`: play them in order. */
|
|
105
|
+
selector?: Selector;
|
|
106
|
+
/**
|
|
107
|
+
* For the memoried `sequence` selector (spec §7): is the selector's cursor
|
|
108
|
+
* SHARED across all flows (one cursor world-wide - e.g. two NPCs never draw the
|
|
109
|
+
* same shuffled line) or kept per-flow? Default `false` (per-flow). Orthogonal to
|
|
110
|
+
* a property's `shared` flag; same name, same idea.
|
|
111
|
+
*/
|
|
112
|
+
shared?: boolean;
|
|
113
|
+
/** `sequence` config (order × exhaust, spec §4). */
|
|
114
|
+
options?: SequenceOptions;
|
|
115
|
+
children: Array<Group | Snippet>;
|
|
116
|
+
gameData?: GameData;
|
|
117
|
+
/** Author tags (#215). See LineBeat.tags. */
|
|
118
|
+
tags?: string[];
|
|
119
|
+
/** The choice text (spec §5): a single line | text beat, always present on an
|
|
120
|
+
* authored option. IS the host's choice text - no derivation, no look-ahead. */
|
|
121
|
+
prompt?: PromptBeat;
|
|
122
|
+
/** Keep this option out of `getChoices()` while ineligible (secrecy). Default false. */
|
|
123
|
+
secretUntilEligible?: boolean;
|
|
124
|
+
/** Repeatable choice option (spec §5). `true`: always offered while its condition passes (Ink `+`).
|
|
125
|
+
* Default `false`: once-only - after the player follows it once it is gone from `getChoices`
|
|
126
|
+
* entirely (not delivered, not flagged unavailable - just absent; Ink `*`). */
|
|
127
|
+
sticky?: boolean;
|
|
128
|
+
/** The choice's fallback option (spec §5). Never delivered as a normal option; auto-followed the
|
|
129
|
+
* moment it is the ONLY eligible option left (its own condition still applies). At most one per
|
|
130
|
+
* choice. Default `false`. */
|
|
131
|
+
fallback?: boolean;
|
|
132
|
+
}
|
|
133
|
+
interface Block {
|
|
134
|
+
id: string;
|
|
135
|
+
type: "block";
|
|
136
|
+
/** Mandatory, author-editable (jump targets must show a readable destination). */
|
|
137
|
+
name: string;
|
|
138
|
+
/**
|
|
139
|
+
* The author-editable, host-facing ADDRESS (spec §6) - a readable slug the runtime targets
|
|
140
|
+
* ("play this block"), distinct from the opaque immutable `id` (the internal join key). Absent =
|
|
141
|
+
* derived from `name` (`effectiveGameId`); present = author-pinned (survives renames). Unique
|
|
142
|
+
* within its scene (scene-scoped addressing). Hyphen-slug form (see core `gameIdify`).
|
|
143
|
+
*/
|
|
144
|
+
gameId?: string;
|
|
145
|
+
children: Array<Group | Snippet>;
|
|
146
|
+
gameData?: GameData;
|
|
147
|
+
/** Author tags (#215). See LineBeat.tags. */
|
|
148
|
+
tags?: string[];
|
|
149
|
+
}
|
|
150
|
+
interface Scene {
|
|
151
|
+
id: string;
|
|
152
|
+
type: "scene";
|
|
153
|
+
name: string;
|
|
154
|
+
/**
|
|
155
|
+
* The author-editable, host-facing ADDRESS (spec §6) - a readable slug the runtime targets
|
|
156
|
+
* ("play this scene"), distinct from the opaque immutable `id`. Absent = derived from `name`
|
|
157
|
+
* (`effectiveGameId`); present = author-pinned. Unique project-wide. Hyphen-slug form (core `gameIdify`).
|
|
158
|
+
*/
|
|
159
|
+
gameId?: string;
|
|
160
|
+
/** Host metadata - e.g. `location` lives here, not as a core field. */
|
|
161
|
+
gameData?: GameData;
|
|
162
|
+
/** Author tags (#215). See LineBeat.tags. */
|
|
163
|
+
tags?: string[];
|
|
164
|
+
/** Entry behaviour / setup (spec §15). */
|
|
165
|
+
onEntry?: Effect[];
|
|
166
|
+
/**
|
|
167
|
+
* Scene-scoped property declarations - `@scene` (spec §7). Each may be marked
|
|
168
|
+
* `shared` (one value across all flows in the scene) or not (per-flow, the
|
|
169
|
+
* default); the reference is `@scene.name` either way.
|
|
170
|
+
*/
|
|
171
|
+
sceneProps?: PropertyDecl[];
|
|
172
|
+
/** One or more blocks; the first is the default entry. */
|
|
173
|
+
blocks: Block[];
|
|
174
|
+
}
|
|
175
|
+
/** A flow file (.patterflow) - one scene. */
|
|
176
|
+
interface FlowFile {
|
|
177
|
+
schema: string;
|
|
178
|
+
scene: Scene;
|
|
179
|
+
}
|
|
180
|
+
type FlowNode = Scene | Block | Group | Snippet;
|
|
181
|
+
/**
|
|
182
|
+
* Visit every selectable node (group / snippet) under a children list,
|
|
183
|
+
* depth-first in authored order. Generic over the source AND compiled trees
|
|
184
|
+
* (both share the group/snippet shape) - THE one tree walk; validators,
|
|
185
|
+
* indexers, and exporters all route through it so no hand-rolled walker can
|
|
186
|
+
* forget a level (or a string-bearing field on one).
|
|
187
|
+
*/
|
|
188
|
+
declare function walkNodes<N extends {
|
|
189
|
+
type: string;
|
|
190
|
+
}>(nodes: ReadonlyArray<N>, visit: (node: N) => void): void;
|
|
191
|
+
/**
|
|
192
|
+
* A beat with NO content worth keeping: an empty line / text bubble left behind - e.g. a snippet seeded
|
|
193
|
+
* only so a jump could hang off it. Such a beat would render at runtime as a blank line that (lacking any
|
|
194
|
+
* localised string) falls back to emitting its raw id, so the editor drops it on save. `hasText` = the
|
|
195
|
+
* beat has a non-empty display string. A game-event beat is never contentless (it's a pure instruction),
|
|
196
|
+
* and a beat carrying `gameData` or `tags` is meaningful even with no text. A jump-only snippet is then
|
|
197
|
+
* just `{ jump }` with zero beats - which is valid.
|
|
198
|
+
*/
|
|
199
|
+
declare function isContentlessBeat(beat: Beat, hasText: boolean): boolean;
|
|
200
|
+
/** Slugify a name into a hyphen-form game id: lowercase, drop apostrophes, runs of
|
|
201
|
+
* other punctuation -> a single hyphen, collapse repeats, no leading / trailing hyphen. */
|
|
202
|
+
declare function gameIdify(text: string): string;
|
|
203
|
+
/** A valid authored game id: lowercase alphanumerics + hyphens, no leading / trailing hyphen. */
|
|
204
|
+
declare function isValidGameId(gameId: string): boolean;
|
|
205
|
+
/** The effective address: the explicit `gameId` if pinned, else derived from `name`. */
|
|
206
|
+
declare function effectiveGameId(entity: {
|
|
207
|
+
gameId?: string;
|
|
208
|
+
name: string;
|
|
209
|
+
}): string;
|
|
210
|
+
type PropertyType = "boolean" | "number" | "string" | "flags" | "enum";
|
|
211
|
+
interface PropertyDecl {
|
|
212
|
+
name: string;
|
|
213
|
+
type: PropertyType;
|
|
214
|
+
default?: ScalarValue;
|
|
215
|
+
/**
|
|
216
|
+
* The orthogonal *sharing* axis (spec §7): is this property's value shared across
|
|
217
|
+
* all flows (one world value) or kept per-flow? It does NOT change the reference
|
|
218
|
+
* syntax - sharing is set here, on the declaration, not by a different scope token.
|
|
219
|
+
* The default depends on the scope it is declared in: a **global** property
|
|
220
|
+
* (project `properties` -> `@patter`) defaults to **shared**; a **scene-local**
|
|
221
|
+
* property (scene `sceneProps` -> `@scene`) defaults to **not shared** (per-flow).
|
|
222
|
+
*/
|
|
223
|
+
shared?: boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Persistence axis, for **scene-local (`@scene`) properties** (spec §7). Default
|
|
226
|
+
* `false`: the value PERSISTS across scene re-entries (like every other property).
|
|
227
|
+
* `true`: the value is **reseeded to its default on every scene entry** - "fresh
|
|
228
|
+
* each playthrough" (Ink's `temp`). Orthogonal to `shared`. Ignored on global
|
|
229
|
+
* (`@patter`) properties, which always persist for the life of the piece.
|
|
230
|
+
*/
|
|
231
|
+
temporary?: boolean;
|
|
232
|
+
/** For enum / flags. */
|
|
233
|
+
values?: string[];
|
|
234
|
+
/** Free-text author note documenting what this property is for (authoring only; shown as a hint). */
|
|
235
|
+
purpose?: string;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* A property of a host / world scope (`@world`, `@game`, ...). The same shape the
|
|
239
|
+
* `@wildwinter/scoperegistry` `scopeRegistrySpec` uses, declared structurally here
|
|
240
|
+
* so the model stays free of a runtime dependency. `default` seeds the standalone
|
|
241
|
+
* runtime's self-backed bag (see `HostScopeSpec`); `writable: false` makes the
|
|
242
|
+
* property read-only to the story (validated at compile time).
|
|
243
|
+
*/
|
|
244
|
+
interface HostScopeDecl {
|
|
245
|
+
name: string;
|
|
246
|
+
type: PropertyType;
|
|
247
|
+
values?: string[];
|
|
248
|
+
default?: ScalarValue;
|
|
249
|
+
writable?: boolean;
|
|
250
|
+
/** Free-text author note documenting the property (authoring only; shown as a hint). */
|
|
251
|
+
purpose?: string;
|
|
252
|
+
}
|
|
253
|
+
/** One scope (`token`) in a project's host-scope registry. */
|
|
254
|
+
interface HostScopeSpec {
|
|
255
|
+
/** The scope token after `@` (e.g. `"world"`). Must not collide with Patter's own
|
|
256
|
+
* `patter` / `scene` / `flow`. */
|
|
257
|
+
token: string;
|
|
258
|
+
/** Scope-level read/write default for its declarations (default true). */
|
|
259
|
+
writable?: boolean;
|
|
260
|
+
/** Property declarations; omit for an opaque scope (any name, unchecked). */
|
|
261
|
+
declarations?: HostScopeDecl[];
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* A project's host-scope registry: the `scopeRegistrySpec` it OWNS (spec
|
|
265
|
+
* design/scope-registry.md §6). Makes `@world` (and any host scope) first-class
|
|
266
|
+
* in a standalone project: the compiler validates references into it, the runtime
|
|
267
|
+
* self-backs it from declaration defaults when no host resolver claims the token,
|
|
268
|
+
* and coverage drives its values. Structurally identical to scoperegistry's
|
|
269
|
+
* `ScopeRegistrySpec` so it threads straight through the compiler.
|
|
270
|
+
*/
|
|
271
|
+
interface HostScopeRegistry {
|
|
272
|
+
version: number;
|
|
273
|
+
scopes: HostScopeSpec[];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* A coverage input driver (#159, design/scope-registry.md §7): during a coverage run the harness feeds a
|
|
277
|
+
* host-scope property (`@world.x`) values from `values` so branches gated on external state get exercised.
|
|
278
|
+
* @world is the game-engine seam: @patter / @scene are story-owned and covered for free, so are never driven.
|
|
279
|
+
*/
|
|
280
|
+
interface CoverageDriver {
|
|
281
|
+
/** The host-scope ref to drive, e.g. `"@world.phase"`. */
|
|
282
|
+
ref: string;
|
|
283
|
+
/** `"initial"`: set once when each playthrough starts. `"recurring"`: re-rolled at choice points so a
|
|
284
|
+
* single run can pass through several world states. */
|
|
285
|
+
kind: "initial" | "recurring";
|
|
286
|
+
/** For a recurring driver: how often to re-roll at a choice point (default `"sometimes"`). */
|
|
287
|
+
cadence?: "rarely" | "sometimes" | "often";
|
|
288
|
+
/** The pool the harness picks from (uniform). Empty = the driver is inert. */
|
|
289
|
+
values: ScalarValue[];
|
|
290
|
+
}
|
|
291
|
+
interface CastMember {
|
|
292
|
+
/** Canonical speaker name (matched by a beat's `character`); language-neutral key. */
|
|
293
|
+
name: string;
|
|
294
|
+
/** Localised player-facing name - a localisation id (project-level strings). */
|
|
295
|
+
displayName?: string;
|
|
296
|
+
/** Free-text production notes about the character (casting, voice, intent) - authoring only. */
|
|
297
|
+
notes?: string;
|
|
298
|
+
/** The voice actor cast for this character, if known - surfaced in the VO script export (spec §16).
|
|
299
|
+
* Authoring-only (not shipped in the runtime bundle). */
|
|
300
|
+
actor?: string;
|
|
301
|
+
gameData?: GameData;
|
|
302
|
+
}
|
|
303
|
+
/** A node TYPE that can carry author-defined gameData fields (the gameData schema, project-level).
|
|
304
|
+
* Beat kinds map straight through: dialogue = `line`, narration = `text`, game event = `gameEvent`. */
|
|
305
|
+
type GameDataNodeKind = "scene" | "block" | "snippet" | "line" | "text" | "gameEvent";
|
|
306
|
+
/** The value type of a gameData field - the property-type vocabulary plus a multiline-text variant.
|
|
307
|
+
* Drives the inspector's editor widget (text / textarea / number / toggle / enum dropdown). */
|
|
308
|
+
type GameDataFieldType = "text" | "multiline" | "number" | "boolean" | "enum";
|
|
309
|
+
/** One author-defined custom field on a node type (host-integration metadata, NOT expression state). */
|
|
310
|
+
interface GameDataField {
|
|
311
|
+
/** Field key - also the key a node stores its value under in `gameData` (values are name-keyed). */
|
|
312
|
+
name: string;
|
|
313
|
+
type: GameDataFieldType;
|
|
314
|
+
/** The value used when a node sets nothing. Storage is SPARSE - nodes hold only their overrides, and
|
|
315
|
+
* a reader falls back to this default (so changing it here propagates to every node that didn't set it). */
|
|
316
|
+
default?: ScalarValue;
|
|
317
|
+
/** Allowed values when `type` is "enum". */
|
|
318
|
+
values?: string[];
|
|
319
|
+
/** Free-text description of what the field is for - shown as a rollover hint in the inspector. */
|
|
320
|
+
purpose?: string;
|
|
321
|
+
}
|
|
322
|
+
/** Author-defined gameData fields, grouped by the node type they attach to (project-level schema). */
|
|
323
|
+
type GameDataFields = Partial<Record<GameDataNodeKind, GameDataField[]>>;
|
|
324
|
+
interface WritingStatusDecl {
|
|
325
|
+
name: string;
|
|
326
|
+
/**
|
|
327
|
+
* Threshold marker: this status AND every later one classify as "ready to
|
|
328
|
+
* record". Declared on exactly one status in the list.
|
|
329
|
+
*/
|
|
330
|
+
readyToRecord?: boolean;
|
|
331
|
+
/** Threshold marker: this status and every later one classify as "ready to ship". */
|
|
332
|
+
readyToShip?: boolean;
|
|
333
|
+
/** Theme character-palette slot (0-11) for the per-line status badge / inspector swatch (Patterpad #196).
|
|
334
|
+
* A slot (not a hex) so it adapts to light/dark + the reading palettes. Absent = no colour (neutral). */
|
|
335
|
+
colour?: number;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* The default writing-status ladder (used when a project declares none). The
|
|
339
|
+
* FIRST status is the level-0 / absence status: a beat with no recorded status
|
|
340
|
+
* counts as `stub`. Nothing is inferred from content - tracking is opt-in, and a
|
|
341
|
+
* project that ignores statuses reads as all-stub (so its burndown / script
|
|
342
|
+
* export carry no weight, by design - spec §13).
|
|
343
|
+
*/
|
|
344
|
+
declare const DEFAULT_WRITING_STATUSES: WritingStatusDecl[];
|
|
345
|
+
/** A recording-status rung (#206): like a writing status but with no readiness markers - the recording
|
|
346
|
+
* ladder is ordered missing -> final and carries a picked palette colour for the inspector chip / report. */
|
|
347
|
+
interface RecordingStatusDecl {
|
|
348
|
+
name: string;
|
|
349
|
+
/** Theme character-palette slot (0-11) for the inspector chip / report bar. Absent = neutral. */
|
|
350
|
+
colour?: number;
|
|
351
|
+
}
|
|
352
|
+
/** The default recording-status ladder (used when a project declares none). The FIRST rung is the
|
|
353
|
+
* level-0 / absence status: a beat with no recorded status counts as `missing`. */
|
|
354
|
+
declare const DEFAULT_RECORDING_STATUSES: RecordingStatusDecl[];
|
|
355
|
+
/** One recording rung mapped to the folder its audio files live in (Audio Folders mode). */
|
|
356
|
+
interface RecordingFolder {
|
|
357
|
+
name: string;
|
|
358
|
+
/** Project-relative folder `<audioRoot>/<slug(name)>`, or undefined for the baseline "not recorded" rung. */
|
|
359
|
+
folder?: string;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Audio Folders (#206): map the recording ladder to its derived folders under a single audio root.
|
|
363
|
+
* Each rung's folder is `<audioRoot>/<slug(name)>`; the FIRST (lowest) rung is the "not recorded"
|
|
364
|
+
* baseline and gets NO folder. The single source of truth shared by the indexer, scratch-save, and the
|
|
365
|
+
* manifest so they never disagree. Returns bare rungs (no folders) when `audioRoot` is empty.
|
|
366
|
+
*/
|
|
367
|
+
declare function deriveRecordingFolders(audioRoot: string | undefined, statuses: RecordingStatusDecl[]): RecordingFolder[];
|
|
368
|
+
/**
|
|
369
|
+
* A class of documentation note (spec §18) - the project-defined vocabulary plus
|
|
370
|
+
* where each class is DELIVERED. The editor (Patterpad) always shows every class;
|
|
371
|
+
* `deliver` governs EXPORTS only. A note inherits down the tree, so a class set
|
|
372
|
+
* on a scene/block flows to its lines (outermost-first).
|
|
373
|
+
*/
|
|
374
|
+
interface DocumentationClass {
|
|
375
|
+
name: string;
|
|
376
|
+
/**
|
|
377
|
+
* Export channels this class flows to: a list of channel names, or `"*"` for
|
|
378
|
+
* all. Omitted/empty = editor-only (e.g. "writing"). Built-in channels: `vo`
|
|
379
|
+
* (voice-recording scripts), `loc` (localisation handoff); studios add their
|
|
380
|
+
* own (e.g. `sfx`, `art`), carried now and picked up when that export exists.
|
|
381
|
+
*/
|
|
382
|
+
deliver?: string[] | "*";
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* The default documentation classes (used when a project declares none). An
|
|
386
|
+
* untyped note (no class) is editor-only by construction; these are the named
|
|
387
|
+
* routes. The vocabulary is project-extensible - a studio replaces this list.
|
|
388
|
+
*/
|
|
389
|
+
declare const DEFAULT_DOCUMENTATION_CLASSES: DocumentationClass[];
|
|
390
|
+
/** The version-control system a project is kept under (spec §12). Drives the lock-aware vs merge-based
|
|
391
|
+
* write path + the emitted VCS config; chosen at create and switchable in Project Settings. */
|
|
392
|
+
type VcsKind = "git" | "perforce" | "plastic" | "svn" | "none";
|
|
393
|
+
/** Spell-check setup for a project (Patterpad #177). */
|
|
394
|
+
interface ProjectDictionary {
|
|
395
|
+
/** The active dictionary id - a built-in language ("en-US"/"en-GB") or an app-level imported Hunspell
|
|
396
|
+
* pair. Absent = derive from the source locale. The imported pair itself lives per-machine (userData),
|
|
397
|
+
* so only the id travels with the project. */
|
|
398
|
+
language?: string;
|
|
399
|
+
/** The project's custom word list - names, places, invented terms - always accepted. Travels with the
|
|
400
|
+
* project (shared via VCS), unlike the per-machine imported dictionaries. */
|
|
401
|
+
words?: string[];
|
|
402
|
+
/** Words the author chose to IGNORE (right-click ▸ Ignore on a flagged word). Distinct from `words`:
|
|
403
|
+
* these aren't vocabulary to add, just tokens to stop flagging in this project (a dialect spelling, a
|
|
404
|
+
* code). Both suppress the squiggle; they differ in intent + where they surface. Travels with the project. */
|
|
405
|
+
ignore?: string[];
|
|
406
|
+
/** Spell-check on/off for this project (default on). */
|
|
407
|
+
enabled?: boolean;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Estimating (writing-burndown, spec §13): when a scene is still all guesswork - every status-tracked
|
|
411
|
+
* beat at or below `thresholdStatus` (an unset beat counts as the lowest rung) - the production report
|
|
412
|
+
* REPLACES its actual (placeholder) line count with an estimate, and shares that estimate across the
|
|
413
|
+
* characters appearing in its placeholder lines. Off by default; when off, no estimate appears anywhere.
|
|
414
|
+
* See design/proposals/estimating.md.
|
|
415
|
+
*/
|
|
416
|
+
interface EstimatingConfig {
|
|
417
|
+
/** Master on/off. Off (or absent) = the report shows pure actuals, no estimate anywhere. */
|
|
418
|
+
enabled: boolean;
|
|
419
|
+
/** Writing-ladder rung NAME: a scene is estimated only when EVERY status-tracked beat is at or below
|
|
420
|
+
* this rung. Absent = the lowest rung. */
|
|
421
|
+
thresholdStatus?: string;
|
|
422
|
+
/** The per-scene estimate (written lines) used when no tag override matches. */
|
|
423
|
+
defaultLines: number;
|
|
424
|
+
/** Tag -> lines overrides. A scene carrying a mapped tag uses that number; if it carries several, the
|
|
425
|
+
* LARGEST wins. */
|
|
426
|
+
tagEstimates?: {
|
|
427
|
+
tag: string;
|
|
428
|
+
lines: number;
|
|
429
|
+
}[];
|
|
430
|
+
}
|
|
431
|
+
interface ProjectFile {
|
|
432
|
+
schema: string;
|
|
433
|
+
root?: boolean;
|
|
434
|
+
project: {
|
|
435
|
+
id: string;
|
|
436
|
+
name: string;
|
|
437
|
+
roomKey?: string;
|
|
438
|
+
};
|
|
439
|
+
locales: {
|
|
440
|
+
default: string;
|
|
441
|
+
all: string[];
|
|
442
|
+
};
|
|
443
|
+
/** The authored entry point: the scene (and optional block) a flow starts at when none is given,
|
|
444
|
+
* used by `patter play`, Patterpad's Play, and coverage. Omitted = fall back to the first scene. */
|
|
445
|
+
start?: {
|
|
446
|
+
scene: string;
|
|
447
|
+
block?: string;
|
|
448
|
+
};
|
|
449
|
+
/** The authored scene order (scene ids) for navigation. Scenes not listed follow in file order;
|
|
450
|
+
* listed ids that no longer exist are ignored. Omitted = plain file order. Presentation only:
|
|
451
|
+
* it never affects play, which always starts from `start` / an explicit address. */
|
|
452
|
+
sceneOrder?: string[];
|
|
453
|
+
/** Version-control system (spec §12); omitted = unset / none. */
|
|
454
|
+
vcs?: VcsKind;
|
|
455
|
+
/** Project-wide VO mode (spec §16) - one boolean, no per-scene override. */
|
|
456
|
+
voiced?: boolean;
|
|
457
|
+
/** Track audio/recording status (#206): whether the recording-status ladder, Audio Folders, scratch, and
|
|
458
|
+
* the inspector's Audio row + the report's recording breakdown are active. Only meaningful for a `voiced`
|
|
459
|
+
* project (it gates on both). Authoring metadata, editor-only; never reaches the bundle. Omitted = OFF -
|
|
460
|
+
* opt-in even for a voiced project (a voiced story may want voice scripts without tracking recording status). */
|
|
461
|
+
trackAudioStatus?: boolean;
|
|
462
|
+
/**
|
|
463
|
+
* Inline text formatting. When true, authors can mark dialogue / narration / direction /
|
|
464
|
+
* choice-prompt text bold, italic, or bold+italic; it is stored INSIDE the localised strings
|
|
465
|
+
* (and the flow's `direction`) as `<b>…</b>`, `<i>…</i>`, `<bi>…</bi>`, with literal `<`, `>`,
|
|
466
|
+
* `&` escaped to `<` / `>` / `&`. The runtime treats the string as opaque - it is the
|
|
467
|
+
* GAME's job to parse the tags. Default ON (omitted === enabled); set `false` to disable it for a
|
|
468
|
+
* game that renders plain strings and would otherwise show the tags literally. */
|
|
469
|
+
formatting?: boolean;
|
|
470
|
+
/** Autosave: periodically persist the edited scene without an explicit Save. Default ON (omitted ===
|
|
471
|
+
* enabled); set `false` to require manual saves. */
|
|
472
|
+
autosave?: boolean;
|
|
473
|
+
/** Closed captions (#214): the delimiter pair that wraps non-spoken caption cues inside DIALOGUE
|
|
474
|
+
* lines (e.g. `(sigh)` in `Oh dear. (sigh) What now?`). A game can turn captions off at runtime
|
|
475
|
+
* (`setClosedCaptions(false)`), and the runtime then strips every `open…close` span - delimiters and
|
|
476
|
+
* surrounding whitespace - from line text. Baked into the bundle so the runtime knows the pair;
|
|
477
|
+
* omitted = the default `(` / `)`. Captions are ON by default (full text shown). */
|
|
478
|
+
closedCaptions?: CaptionDelimiters;
|
|
479
|
+
audio?: {
|
|
480
|
+
scratchStore: string;
|
|
481
|
+
};
|
|
482
|
+
layout?: {
|
|
483
|
+
flow?: string;
|
|
484
|
+
strings?: string;
|
|
485
|
+
authoring?: string;
|
|
486
|
+
};
|
|
487
|
+
/** `bundle`: the compiled `.patterc` output path (relative to the project root,
|
|
488
|
+
* or absolute); default `dist/<project-file-stem>.patterc` (spec §11).
|
|
489
|
+
* `localisation`: how strings ship + are resolved (spec §11).
|
|
490
|
+
* - "embedded" (default): every locale's strings live INSIDE the `.patterc`; the runtime resolves
|
|
491
|
+
* them and can switch locale live (`setLocale`).
|
|
492
|
+
* - "ids": the `.patterc` carries NO strings; the runtime emits the beat ID for each line and the
|
|
493
|
+
* game looks it up in its own loc system (Export Localisation hands over the language files).
|
|
494
|
+
* `sourceDebug` embeds the SOURCE language too, purely so the build can be played for debugging;
|
|
495
|
+
* the bundle flags it so the runtime can warn it is not a shippable build. */
|
|
496
|
+
export?: {
|
|
497
|
+
targets?: string[];
|
|
498
|
+
bundle?: string;
|
|
499
|
+
localisation?: {
|
|
500
|
+
mode: "embedded" | "ids";
|
|
501
|
+
sourceDebug?: boolean;
|
|
502
|
+
};
|
|
503
|
+
};
|
|
504
|
+
properties?: PropertyDecl[];
|
|
505
|
+
/** Host / world scopes the project declares (`@world`, `@game`, ...): makes them first-class so the
|
|
506
|
+
* compiler validates references into them, the runtime self-backs them from defaults when no host
|
|
507
|
+
* resolver is bound, and coverage drives their values. Omitted = no host scopes (`@world.x` is then a
|
|
508
|
+
* compile error). See design/scope-registry.md §6. */
|
|
509
|
+
scopeRegistry?: HostScopeRegistry;
|
|
510
|
+
/** Coverage input drivers (#159): values to feed host scopes (`@world`) during a coverage run so
|
|
511
|
+
* externally-gated branches get exercised. Authoring-only (never reaches the runtime bundle). */
|
|
512
|
+
coverageDrivers?: CoverageDriver[];
|
|
513
|
+
cast?: CastMember[];
|
|
514
|
+
gameDataFields?: GameDataFields;
|
|
515
|
+
/** Ordered writing-status ladder (not-done -> done); default `DEFAULT_WRITING_STATUSES`. */
|
|
516
|
+
writingStatuses?: WritingStatusDecl[];
|
|
517
|
+
/** Ordered recording-status ladder (not-done -> done); default `DEFAULT_RECORDING_STATUSES`. */
|
|
518
|
+
recordingStatuses?: RecordingStatusDecl[];
|
|
519
|
+
/** Audio Folders mode (#206): the single project-relative root under which each rung's audio lives, in
|
|
520
|
+
* an auto-derived subfolder `<audioRoot>/<slug(statusName)>/` (see `deriveRecordingFolders`). Only
|
|
521
|
+
* meaningful when `audioFolders` is on. Authoring metadata; never reaches the bundle. */
|
|
522
|
+
audioRoot?: string;
|
|
523
|
+
/** Audio Folders mode (#206): when true, a dialogue line's recording status is DERIVED from which
|
|
524
|
+
* rung's derived folder (under `audioRoot`) holds its `<beatId>.wav|mp3` (top-down the ladder, implicit
|
|
525
|
+
* "missing"), instead of being set manually. Authoring metadata; never reaches the bundle. Default off. */
|
|
526
|
+
audioFolders?: boolean;
|
|
527
|
+
/** Scratch recording (Patterpad #224): the recording-status rung whose folder is the source/dest for
|
|
528
|
+
* in-app "record scratch" takes. When set (and `audioFolders` on), Patterpad offers to record a quick
|
|
529
|
+
* scratch take into this rung's folder for any line at or below this rung. Authoring metadata, editor-
|
|
530
|
+
* only; never reaches the bundle. Unset = scratch recording off. */
|
|
531
|
+
scratchStatus?: string;
|
|
532
|
+
/** Spell-check setup (Patterpad #177): the active dictionary language + the project's custom word list +
|
|
533
|
+
* an on/off flag. Source-language-only, authoring metadata - it never reaches the runtime bundle. */
|
|
534
|
+
dictionary?: ProjectDictionary;
|
|
535
|
+
/**
|
|
536
|
+
* Estimating (spec §13 writing burndown): replace a still-guesswork scene's actual (placeholder)
|
|
537
|
+
* line count with an estimate in the production report. Off by default. See `EstimatingConfig` and
|
|
538
|
+
* design/proposals/estimating.md.
|
|
539
|
+
*/
|
|
540
|
+
estimating?: EstimatingConfig;
|
|
541
|
+
/** Documentation-note classes + their export routing (spec §18); default `DEFAULT_DOCUMENTATION_CLASSES`. */
|
|
542
|
+
documentationClasses?: DocumentationClass[];
|
|
543
|
+
}
|
|
544
|
+
interface LocaleFile {
|
|
545
|
+
schema: string;
|
|
546
|
+
/** Scene id this file's strings belong to (or a project-level marker). */
|
|
547
|
+
scene: string;
|
|
548
|
+
locale: string;
|
|
549
|
+
default?: boolean;
|
|
550
|
+
/** beatId -> text. */
|
|
551
|
+
strings: Record<string, string>;
|
|
552
|
+
}
|
|
553
|
+
/** The `scene` marker for a project-level loc shard (`loc/<locale>/_project.patterloc`): strings that
|
|
554
|
+
* aren't tied to a scene beat - currently cast display names, later project title / UI strings. */
|
|
555
|
+
declare const PROJECT_LOCALE_SCENE = "@project";
|
|
556
|
+
/** The project-level loc-string key for a cast member's player-facing name, e.g. `cast:BARKEEP`.
|
|
557
|
+
* Namespaced so it can't collide with opaque beat ids. The default-locale value is seeded from the
|
|
558
|
+
* CastMember's `displayName`; the runtime resolves a character's shown name through this key. */
|
|
559
|
+
declare function castStringKey(name: string): string;
|
|
560
|
+
/** Typed documentation annotation line (spec §18). */
|
|
561
|
+
interface DocLine {
|
|
562
|
+
/** The documentation CLASS (a `DocumentationClass.name`) - routes export
|
|
563
|
+
* visibility. Omitted = editor-only (not delivered to any export). */
|
|
564
|
+
type?: string;
|
|
565
|
+
text: string;
|
|
566
|
+
}
|
|
567
|
+
/** One message in a threaded editor comment: who wrote it, when (ISO timestamp), and the text. */
|
|
568
|
+
interface CommentMessage {
|
|
569
|
+
author: string;
|
|
570
|
+
ts: string;
|
|
571
|
+
body: string;
|
|
572
|
+
}
|
|
573
|
+
/** A sub-text range a comment is pinned to, within its anchor node's say text (#148). Offsets are
|
|
574
|
+
* character positions over the rendered (plain) source text - inline formatting is marks, not part of
|
|
575
|
+
* the count. `quote` is the text the range covered when made: the editor re-anchors by FINDING it (the
|
|
576
|
+
* offsets are just a hint), and a thread whose quote no longer exists is shown demoted, not lost. */
|
|
577
|
+
interface CommentRange {
|
|
578
|
+
from: number;
|
|
579
|
+
to: number;
|
|
580
|
+
quote: string;
|
|
581
|
+
}
|
|
582
|
+
/** Threaded editor comment (collaboration), anchored to a stable beat/node id. `messages[0]` is the
|
|
583
|
+
* opener; replies follow in order (each carries its own author + timestamp, Word/Docs style).
|
|
584
|
+
* `range` pins it to a span of the node's text (absent = the whole beat). `resolved` archives the
|
|
585
|
+
* thread - hidden in the editor unless "show resolved comments" is on. */
|
|
586
|
+
interface Comment {
|
|
587
|
+
id: string;
|
|
588
|
+
/** Anchored to a stable beat/node id. */
|
|
589
|
+
anchor: string;
|
|
590
|
+
/** A sub-text span within the anchor's text; absent = a whole-beat comment. */
|
|
591
|
+
range?: CommentRange;
|
|
592
|
+
resolved?: boolean;
|
|
593
|
+
messages: CommentMessage[];
|
|
594
|
+
}
|
|
595
|
+
/** A "suggest a rewrite" proposal for a single say/prose beat (review flow, design/proposals/
|
|
596
|
+
* suggest-rewrite.md). Whole-beat anchored: `baseline` is the say text WHEN suggested (the "before" +
|
|
597
|
+
* the drift detector - if it no longer matches the live text, the line changed since and the suggestion
|
|
598
|
+
* is shown stale), `proposed` is the replacement. Accept overwrites the beat's say text; both accept and
|
|
599
|
+
* reject set `resolved` (archived) + `outcome` (a light audit trail). Stored in the authoring shard,
|
|
600
|
+
* never in the flow text, so downstream tools ignore it. */
|
|
601
|
+
interface Suggestion {
|
|
602
|
+
id: string;
|
|
603
|
+
/** The say/prose beat's stable id. */
|
|
604
|
+
anchor: string;
|
|
605
|
+
/** The say text at the moment this was suggested (the diff "before" + staleness check). */
|
|
606
|
+
baseline: string;
|
|
607
|
+
/** The proposed replacement say text. */
|
|
608
|
+
proposed: string;
|
|
609
|
+
author: string;
|
|
610
|
+
ts: string;
|
|
611
|
+
/** Accepted or rejected -> archived (hidden unless "show resolved suggestions" is on). */
|
|
612
|
+
resolved?: boolean;
|
|
613
|
+
outcome?: "accepted" | "rejected";
|
|
614
|
+
}
|
|
615
|
+
interface EditRecord {
|
|
616
|
+
modifiedAt?: string;
|
|
617
|
+
by?: string;
|
|
618
|
+
/** Per-locale localisation date -> staleness when source modifiedAt is later. */
|
|
619
|
+
localisedAt?: Record<string, string>;
|
|
620
|
+
}
|
|
621
|
+
interface AuthoringFile {
|
|
622
|
+
schema: string;
|
|
623
|
+
comments?: Comment[];
|
|
624
|
+
/** "Suggest a rewrite" review proposals (design/proposals/suggest-rewrite.md). */
|
|
625
|
+
suggestions?: Suggestion[];
|
|
626
|
+
/** Typed documentation, keyed by node/beat id (spec §18). */
|
|
627
|
+
documentation?: Record<string, DocLine[]>;
|
|
628
|
+
/**
|
|
629
|
+
* Writing status, keyed by BEAT id - a value from the project's writing-status
|
|
630
|
+
* enumeration (spec §13; `writingStatuses`, default `DEFAULT_WRITING_STATUSES`).
|
|
631
|
+
* Tracked on the source language only (translation staleness is `localisedAt`).
|
|
632
|
+
*/
|
|
633
|
+
writing?: Record<string, string>;
|
|
634
|
+
/**
|
|
635
|
+
* Recording status, keyed by BEAT id - a value from the project's recording-status
|
|
636
|
+
* enumeration (spec §13/§16; `recordingStatuses`). Single-locale by design: games
|
|
637
|
+
* recording VO in more than one language are rare (and absent at indie level); if
|
|
638
|
+
* ever needed, a per-locale extension comes later.
|
|
639
|
+
*/
|
|
640
|
+
recording?: Record<string, string>;
|
|
641
|
+
/**
|
|
642
|
+
* Audio-relationship metadata, keyed by BEAT id. Native (recording) language
|
|
643
|
+
* only - like recording status, VO is single-language by design.
|
|
644
|
+
*/
|
|
645
|
+
audio?: Record<string, unknown>;
|
|
646
|
+
/** Author trail + edit/localisation dates, keyed by beat/node id. */
|
|
647
|
+
edits?: Record<string, EditRecord>;
|
|
648
|
+
/**
|
|
649
|
+
* **Cut** content (spec §13): scene or beat ids removed from the production
|
|
650
|
+
* but kept in source. Orthogonal to status (cut is not a degree of doneness);
|
|
651
|
+
* reports exclude cut content from counts / estimates / coverage and surface
|
|
652
|
+
* it as a separate "cut: N" figure so a removal is visible, not vanished.
|
|
653
|
+
*/
|
|
654
|
+
cut?: Record<string, boolean>;
|
|
655
|
+
}
|
|
656
|
+
/** A compiled expression envelope: canonical source + pre-derived tagged-tuple AST. */
|
|
657
|
+
interface Expression {
|
|
658
|
+
src: string;
|
|
659
|
+
ast: AstNode;
|
|
660
|
+
}
|
|
661
|
+
type CompiledEffect = {
|
|
662
|
+
kind: "set";
|
|
663
|
+
target: string;
|
|
664
|
+
value: Expression;
|
|
665
|
+
};
|
|
666
|
+
interface CompiledSnippet {
|
|
667
|
+
id: string;
|
|
668
|
+
type: "snippet";
|
|
669
|
+
condition?: Expression;
|
|
670
|
+
beats?: Beat[];
|
|
671
|
+
onEnter?: CompiledEffect[];
|
|
672
|
+
onExit?: CompiledEffect[];
|
|
673
|
+
gameData?: GameData;
|
|
674
|
+
tags?: string[];
|
|
675
|
+
jump?: Jump;
|
|
676
|
+
secretUntilEligible?: boolean;
|
|
677
|
+
/** Option-position: repeatable (spec §5). Default false = once-only. */
|
|
678
|
+
sticky?: boolean;
|
|
679
|
+
/** Option-position: the choice's fallback, auto-followed when last (spec §5). */
|
|
680
|
+
fallback?: boolean;
|
|
681
|
+
}
|
|
682
|
+
interface CompiledGroup {
|
|
683
|
+
id: string;
|
|
684
|
+
type: "group";
|
|
685
|
+
condition?: Expression;
|
|
686
|
+
/** Default (omitted) = `"run"`. */
|
|
687
|
+
selector?: Selector;
|
|
688
|
+
/** Selector cursor shared across flows (default false = per-flow). */
|
|
689
|
+
shared?: boolean;
|
|
690
|
+
options?: SequenceOptions;
|
|
691
|
+
children: Array<CompiledGroup | CompiledSnippet>;
|
|
692
|
+
gameData?: GameData;
|
|
693
|
+
tags?: string[];
|
|
694
|
+
/** Option-position fields (spec §5) - only when a direct child of a `choice`. */
|
|
695
|
+
prompt?: PromptBeat;
|
|
696
|
+
secretUntilEligible?: boolean;
|
|
697
|
+
/** Repeatable (spec §5). Default false = once-only. */
|
|
698
|
+
sticky?: boolean;
|
|
699
|
+
/** The choice's fallback, auto-followed when last (spec §5). */
|
|
700
|
+
fallback?: boolean;
|
|
701
|
+
}
|
|
702
|
+
interface CompiledBlock {
|
|
703
|
+
id: string;
|
|
704
|
+
type: "block";
|
|
705
|
+
name: string;
|
|
706
|
+
/** Host-facing address (spec §6); the runtime resolves it to `id`. Absent = derived from `name`. */
|
|
707
|
+
gameId?: string;
|
|
708
|
+
children: Array<CompiledGroup | CompiledSnippet>;
|
|
709
|
+
gameData?: GameData;
|
|
710
|
+
tags?: string[];
|
|
711
|
+
}
|
|
712
|
+
interface CompiledScene {
|
|
713
|
+
id: string;
|
|
714
|
+
type: "scene";
|
|
715
|
+
name: string;
|
|
716
|
+
/** Host-facing address (spec §6); the runtime resolves it to `id`. Absent = derived from `name`. */
|
|
717
|
+
gameId?: string;
|
|
718
|
+
gameData?: GameData;
|
|
719
|
+
tags?: string[];
|
|
720
|
+
onEntry?: CompiledEffect[];
|
|
721
|
+
sceneProps?: PropertyDecl[];
|
|
722
|
+
blocks: CompiledBlock[];
|
|
723
|
+
}
|
|
724
|
+
interface Bundle {
|
|
725
|
+
schema: string;
|
|
726
|
+
/** `hash` fingerprints the WHOLE bundle (binds saves, gates staleness); `structureHash` is the same
|
|
727
|
+
* fingerprint with the string tables left out, so same structureHash + a different hash = a
|
|
728
|
+
* text-only edit, safe to hot-swap in place (live bundle refresh). */
|
|
729
|
+
content: {
|
|
730
|
+
project: string;
|
|
731
|
+
version?: string;
|
|
732
|
+
hash?: string;
|
|
733
|
+
structureHash?: string;
|
|
734
|
+
};
|
|
735
|
+
voiced: boolean;
|
|
736
|
+
locales: {
|
|
737
|
+
default: string;
|
|
738
|
+
included: string[];
|
|
739
|
+
};
|
|
740
|
+
cast?: CastMember[];
|
|
741
|
+
properties?: PropertyDecl[];
|
|
742
|
+
/** Host / world scope declarations, baked from the project so the runtime can self-back a declared
|
|
743
|
+
* scope (`@world`, ...) when no host resolver claims its token. Absent = no host scopes. */
|
|
744
|
+
scopeRegistry?: HostScopeRegistry;
|
|
745
|
+
gameDataFields?: GameDataFields;
|
|
746
|
+
scenes: Record<string, CompiledScene>;
|
|
747
|
+
/** locale -> (beatId -> text). In "embedded" localisation this carries every included locale; in "ids"
|
|
748
|
+
* it is EMPTY (the runtime emits beat IDs), unless `localisation.sourceDebug` embedded the source locale
|
|
749
|
+
* for debug playback. `content.hash` is computed over the FULL strings regardless, so the staleness gate
|
|
750
|
+
* is unaffected. */
|
|
751
|
+
strings: Record<string, Record<string, string>>;
|
|
752
|
+
/** How strings ship + resolve (spec §11). Absent = "embedded" (back-compat default): the runtime resolves
|
|
753
|
+
* `strings` per locale. "ids": the runtime emits beat IDs and the game localises them itself; `sourceDebug`
|
|
754
|
+
* means the source locale is embedded purely for debug playback and the runtime should flag the build as
|
|
755
|
+
* not shippable. */
|
|
756
|
+
localisation?: {
|
|
757
|
+
mode: "embedded" | "ids";
|
|
758
|
+
sourceDebug?: boolean;
|
|
759
|
+
};
|
|
760
|
+
/** Closed-caption delimiters baked from the project (#214). Absent = the default `(` / `)`; the
|
|
761
|
+
* runtime strips spans between them from line text when a game disables captions. */
|
|
762
|
+
closedCaptions?: CaptionDelimiters;
|
|
763
|
+
}
|
|
764
|
+
/** Closed-caption configuration (#214). `open`/`close` wrap a caption cue inside a dialogue line (both
|
|
765
|
+
* non-empty; they MAY be the same token, e.g. `*…*`). `character` names a cast member whose lines are a
|
|
766
|
+
* pure caption: when captions are off, ALL of that character's dialogue (and its speaker label) is
|
|
767
|
+
* omitted - delimiters or not - leaving a silent line that still fires (so audio plays). Absent / empty
|
|
768
|
+
* `character` resolves to the default `SFX` (you "disable" it simply by never using that speaker). */
|
|
769
|
+
interface CaptionDelimiters {
|
|
770
|
+
open: string;
|
|
771
|
+
close: string;
|
|
772
|
+
character?: string;
|
|
773
|
+
}
|
|
774
|
+
/** The default caption delimiters when a project pins none: square brackets, the closed-captioning
|
|
775
|
+
* convention for non-speech cues. Round brackets are deliberately NOT the default: `(` at the start of a
|
|
776
|
+
* line opens a performance direction in the editor, so it would shadow a caption cue there. */
|
|
777
|
+
declare const DEFAULT_CAPTION_DELIMITERS: CaptionDelimiters;
|
|
778
|
+
/** The default caption character: a cast member named `SFX` whose lines are pure captions (omitted when
|
|
779
|
+
* captions are off). Applies even to a project that pins no `closedCaptions`. */
|
|
780
|
+
declare const DEFAULT_CAPTION_CHARACTER = "SFX";
|
|
781
|
+
|
|
782
|
+
export { type AuthoringFile, type Beat, type Block, type Bundle, 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_RECORDING_STATUSES, DEFAULT_WRITING_STATUSES, type DocLine, type DocumentationClass, type EditRecord, type Effect, type EstimatingConfig, type Expression, type FlowFile, type FlowNode, type GameData, type GameDataField, type GameDataFieldType, type GameDataFields, type GameDataNodeKind, type GameEventBeat, type Group, type HostScopeDecl, type HostScopeRegistry, type HostScopeSpec, type Jump, type JumpTarget, type LineBeat, type LocaleFile, PROJECT_LOCALE_SCENE, type ProjectDictionary, type ProjectFile, type PromptBeat, type PropertyDecl, type PropertyType, type RecordingFolder, type RecordingStatusDecl, type ScalarValue, type Scene, type Selector, type SelectorExhaust, type SelectorOrder, type SequenceOptions, type Snippet, type Suggestion, type TextBeat, type VcsKind, type WritingStatusDecl, castStringKey, deriveRecordingFolders, effectiveGameId, gameIdify, isContentlessBeat, isValidGameId, walkNodes };
|