@rohal12/spindle 0.40.1 → 0.42.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/pkg/format.js +1 -1
- package/package.json +1 -1
- package/src/action-registry.ts +3 -11
- package/src/event-emitter.ts +71 -0
- package/src/index.tsx +3 -2
- package/src/store.ts +28 -41
- package/src/story-api.ts +45 -78
- package/src/types-drift-check.ts +15 -0
- package/types/index.d.ts +332 -4
package/types/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface HistoryMoment {
|
|
|
14
14
|
passage: string;
|
|
15
15
|
variables: Record<string, unknown>;
|
|
16
16
|
timestamp: number;
|
|
17
|
+
prng?: { seed: string; pull: number } | null;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -27,6 +28,7 @@ export interface SavePayload {
|
|
|
27
28
|
historyIndex: number;
|
|
28
29
|
visitCounts?: Record<string, number>;
|
|
29
30
|
renderCounts?: Record<string, number>;
|
|
31
|
+
prng?: { seed: string; pull: number } | null;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -79,6 +81,9 @@ export interface SettingsAPI {
|
|
|
79
81
|
addList(name: string, config: ListConfig): void;
|
|
80
82
|
addRange(name: string, config: RangeConfig): void;
|
|
81
83
|
get(name: string): unknown;
|
|
84
|
+
getToggle(name: string): boolean;
|
|
85
|
+
getList(name: string): string;
|
|
86
|
+
getRange(name: string): number;
|
|
82
87
|
set(name: string, value: unknown): void;
|
|
83
88
|
getAll(): Record<string, unknown>;
|
|
84
89
|
getDefinitions(): Map<string, SettingDef>;
|
|
@@ -102,6 +107,238 @@ export interface Passage {
|
|
|
102
107
|
content: string;
|
|
103
108
|
}
|
|
104
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Map of story event names to their callback signatures.
|
|
112
|
+
* @see {@link ../../src/event-emitter.ts} for the implementation.
|
|
113
|
+
*/
|
|
114
|
+
export interface StoryEventMap {
|
|
115
|
+
storyinit: () => void;
|
|
116
|
+
beforerestart: () => void;
|
|
117
|
+
actionsChanged: () => void;
|
|
118
|
+
variableChanged: (
|
|
119
|
+
changed: Record<string, { from: unknown; to: unknown }>,
|
|
120
|
+
) => void;
|
|
121
|
+
beforesave: (
|
|
122
|
+
slot: string | undefined,
|
|
123
|
+
custom: Record<string, unknown> | undefined,
|
|
124
|
+
) => void;
|
|
125
|
+
aftersave: (slot: string | undefined) => void;
|
|
126
|
+
beforeload: (slot: string | undefined) => void;
|
|
127
|
+
afterload: (slot: string | undefined) => void;
|
|
128
|
+
beforenavigate: (passageName: string) => void;
|
|
129
|
+
afternavigate: (to: string, from: string) => void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Event name that can be passed to `Story.on()`. */
|
|
133
|
+
export type StoryEvent = keyof StoryEventMap;
|
|
134
|
+
|
|
135
|
+
/** Callback type for a given story event. */
|
|
136
|
+
export type StoryEventCallback<E extends StoryEvent> = StoryEventMap[E];
|
|
137
|
+
|
|
138
|
+
/** Transition animation type. */
|
|
139
|
+
export type TransitionType = 'none' | 'fade' | 'fade-through' | 'crossfade';
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Configuration for passage transitions.
|
|
143
|
+
* @see {@link ../../src/transition.ts} for the implementation.
|
|
144
|
+
*/
|
|
145
|
+
export interface TransitionConfig {
|
|
146
|
+
type: TransitionType;
|
|
147
|
+
duration?: number;
|
|
148
|
+
pause?: number;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Options for `Story.watch()` trigger registration.
|
|
153
|
+
* @see {@link ../../src/triggers.ts} for the implementation.
|
|
154
|
+
*/
|
|
155
|
+
export interface WatchOptions {
|
|
156
|
+
goto?: string;
|
|
157
|
+
dialog?: string;
|
|
158
|
+
run?: string;
|
|
159
|
+
once?: boolean;
|
|
160
|
+
name?: string;
|
|
161
|
+
priority?: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Type of interactive action registered by a macro. */
|
|
165
|
+
export type ActionType =
|
|
166
|
+
| 'link'
|
|
167
|
+
| 'button'
|
|
168
|
+
| 'cycle'
|
|
169
|
+
| 'textbox'
|
|
170
|
+
| 'numberbox'
|
|
171
|
+
| 'textarea'
|
|
172
|
+
| 'checkbox'
|
|
173
|
+
| 'radiobutton'
|
|
174
|
+
| 'listbox'
|
|
175
|
+
| 'back'
|
|
176
|
+
| 'forward'
|
|
177
|
+
| 'restart'
|
|
178
|
+
| 'save'
|
|
179
|
+
| 'load'
|
|
180
|
+
| 'dialog';
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A registered interactive action (link, button, input, etc.).
|
|
184
|
+
* @see {@link ../../src/action-registry.ts} for the implementation.
|
|
185
|
+
*/
|
|
186
|
+
export interface StoryAction {
|
|
187
|
+
id: string;
|
|
188
|
+
type: ActionType;
|
|
189
|
+
label: string;
|
|
190
|
+
target?: string;
|
|
191
|
+
variable?: string;
|
|
192
|
+
options?: string[];
|
|
193
|
+
value?: unknown;
|
|
194
|
+
disabled?: boolean;
|
|
195
|
+
perform: (value?: unknown) => void;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Storage usage information returned by `Story.storage.getInfo()`.
|
|
200
|
+
* @see {@link ../../src/saves/types.ts} for the implementation.
|
|
201
|
+
*/
|
|
202
|
+
export interface StorageInfo {
|
|
203
|
+
saveCount: number;
|
|
204
|
+
playthroughCount: number;
|
|
205
|
+
totalBytes: number;
|
|
206
|
+
backend: 'indexeddb' | 'localstorage' | 'memory';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Browser storage quota estimate returned by `Story.storage.getQuota()`.
|
|
211
|
+
* @see {@link ../../src/saves/types.ts} for the implementation.
|
|
212
|
+
*/
|
|
213
|
+
export interface StorageQuota {
|
|
214
|
+
usage: number;
|
|
215
|
+
quota: number;
|
|
216
|
+
estimateSupported: boolean;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Parameter metadata for a macro definition.
|
|
221
|
+
* @see {@link ../../src/registry.ts} for the implementation.
|
|
222
|
+
*/
|
|
223
|
+
export interface ParameterDef {
|
|
224
|
+
name: string;
|
|
225
|
+
required?: boolean;
|
|
226
|
+
description?: string;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Metadata about a registered macro, returned by `Story.getMacroRegistry()`.
|
|
231
|
+
* @see {@link ../../src/registry.ts} for the implementation.
|
|
232
|
+
*/
|
|
233
|
+
export interface MacroMetadata {
|
|
234
|
+
name: string;
|
|
235
|
+
block: boolean;
|
|
236
|
+
subMacros: string[];
|
|
237
|
+
storeVar?: boolean;
|
|
238
|
+
interpolate?: boolean;
|
|
239
|
+
merged?: boolean;
|
|
240
|
+
source: 'builtin' | 'user';
|
|
241
|
+
description?: string;
|
|
242
|
+
parameters?: ParameterDef[];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Props passed to a macro's render function.
|
|
247
|
+
* @see {@link ../../src/registry.ts} for the implementation.
|
|
248
|
+
*/
|
|
249
|
+
export interface MacroProps {
|
|
250
|
+
rawArgs: string;
|
|
251
|
+
className?: string;
|
|
252
|
+
id?: string;
|
|
253
|
+
children?: any[];
|
|
254
|
+
branches?: Array<{
|
|
255
|
+
rawArgs: string;
|
|
256
|
+
className?: string;
|
|
257
|
+
id?: string;
|
|
258
|
+
children: any[];
|
|
259
|
+
}>;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Options for registering an interactive action via `ctx.useAction`.
|
|
264
|
+
* @see {@link ../../src/hooks/use-action.ts} for the implementation.
|
|
265
|
+
*/
|
|
266
|
+
export interface UseActionOptions {
|
|
267
|
+
type: ActionType;
|
|
268
|
+
key: string;
|
|
269
|
+
authorId?: string;
|
|
270
|
+
label: string;
|
|
271
|
+
target?: string;
|
|
272
|
+
variable?: string;
|
|
273
|
+
options?: string[];
|
|
274
|
+
value?: unknown;
|
|
275
|
+
disabled?: boolean;
|
|
276
|
+
perform: (value?: unknown) => void;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Context object passed to a macro's render function alongside props.
|
|
281
|
+
* Internal Preact/AST types are represented as `any` since consumers
|
|
282
|
+
* may not have Preact type definitions installed.
|
|
283
|
+
* @see {@link ../../src/define-macro.ts} for the implementation.
|
|
284
|
+
*/
|
|
285
|
+
export interface MacroContext {
|
|
286
|
+
className?: string;
|
|
287
|
+
id?: string;
|
|
288
|
+
resolve?: (s: string | undefined) => string | undefined;
|
|
289
|
+
cls: string;
|
|
290
|
+
mutate: (code: string) => void;
|
|
291
|
+
update: (key: string, value: unknown) => void;
|
|
292
|
+
getValues: () => Record<string, unknown>;
|
|
293
|
+
merged?: readonly [
|
|
294
|
+
Record<string, unknown>,
|
|
295
|
+
Record<string, unknown>,
|
|
296
|
+
Record<string, unknown>,
|
|
297
|
+
];
|
|
298
|
+
varName?: string;
|
|
299
|
+
value?: unknown;
|
|
300
|
+
setValue?: (value: unknown) => void;
|
|
301
|
+
getValue?: () => unknown;
|
|
302
|
+
evaluate?: (expr: string) => unknown;
|
|
303
|
+
collectText: (nodes: any[]) => string;
|
|
304
|
+
sourceLocation: () => string;
|
|
305
|
+
parseVarArgs: (rawArgs: string) => { varName: string; placeholder: string };
|
|
306
|
+
extractOptions: (children: any[]) => string[];
|
|
307
|
+
wrap: (content: any) => any;
|
|
308
|
+
useAction: (opts: UseActionOptions) => string;
|
|
309
|
+
h: (type: any, props: any, ...children: any[]) => any;
|
|
310
|
+
renderNodes: (
|
|
311
|
+
nodes: any[],
|
|
312
|
+
options?: { nobr?: boolean; locals?: Record<string, unknown> },
|
|
313
|
+
) => any;
|
|
314
|
+
renderInlineNodes: (nodes: any[]) => any;
|
|
315
|
+
hooks: {
|
|
316
|
+
useState: any;
|
|
317
|
+
useRef: any;
|
|
318
|
+
useEffect: any;
|
|
319
|
+
useLayoutEffect: any;
|
|
320
|
+
useCallback: any;
|
|
321
|
+
useMemo: any;
|
|
322
|
+
useContext: any;
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Configuration object for `Story.defineMacro()`.
|
|
328
|
+
* @see {@link ../../src/define-macro.ts} for the implementation.
|
|
329
|
+
*/
|
|
330
|
+
export interface MacroDefinition {
|
|
331
|
+
name: string;
|
|
332
|
+
subMacros?: string[];
|
|
333
|
+
block?: boolean;
|
|
334
|
+
interpolate?: boolean;
|
|
335
|
+
merged?: boolean;
|
|
336
|
+
storeVar?: boolean;
|
|
337
|
+
description?: string;
|
|
338
|
+
parameters?: ParameterDef[];
|
|
339
|
+
render: (props: MacroProps, ctx: MacroContext) => any;
|
|
340
|
+
}
|
|
341
|
+
|
|
105
342
|
/**
|
|
106
343
|
* Metadata about a save slot, returned by `getSaveInfo()` and `listSaves()`.
|
|
107
344
|
* @see {@link ../../src/saves/types.ts} for the implementation.
|
|
@@ -166,10 +403,10 @@ export interface StoryAPI {
|
|
|
166
403
|
deleteSave(slot?: string): void;
|
|
167
404
|
|
|
168
405
|
/** Return the number of times a passage has been visited. */
|
|
169
|
-
visited(name
|
|
406
|
+
visited(name?: string): number;
|
|
170
407
|
|
|
171
408
|
/** Check if a passage has been visited at least once. */
|
|
172
|
-
hasVisited(name
|
|
409
|
+
hasVisited(name?: string): boolean;
|
|
173
410
|
|
|
174
411
|
/** Check if any of the given passages have been visited. */
|
|
175
412
|
hasVisitedAny(...names: string[]): boolean;
|
|
@@ -178,10 +415,10 @@ export interface StoryAPI {
|
|
|
178
415
|
hasVisitedAll(...names: string[]): boolean;
|
|
179
416
|
|
|
180
417
|
/** Return the number of times a passage has been rendered. */
|
|
181
|
-
rendered(name
|
|
418
|
+
rendered(name?: string): number;
|
|
182
419
|
|
|
183
420
|
/** Check if a passage has been rendered at least once. */
|
|
184
|
-
hasRendered(name
|
|
421
|
+
hasRendered(name?: string): boolean;
|
|
185
422
|
|
|
186
423
|
/** Check if any of the given passages have been rendered. */
|
|
187
424
|
hasRenderedAny(...names: string[]): boolean;
|
|
@@ -228,4 +465,95 @@ export interface StoryAPI {
|
|
|
228
465
|
|
|
229
466
|
/** Check whether any dialog is currently open. */
|
|
230
467
|
isDialogOpen(): boolean;
|
|
468
|
+
|
|
469
|
+
/** Register a class constructor for use in story expressions. */
|
|
470
|
+
registerClass(name: string, ctor: new (...args: any[]) => any): void;
|
|
471
|
+
|
|
472
|
+
/** Register a custom macro. */
|
|
473
|
+
defineMacro(config: MacroDefinition): void;
|
|
474
|
+
|
|
475
|
+
/** Return metadata for all registered macros. */
|
|
476
|
+
getMacroRegistry(): MacroMetadata[];
|
|
477
|
+
|
|
478
|
+
/** Storage management API. */
|
|
479
|
+
readonly storage: {
|
|
480
|
+
/** Get storage usage information (save count, byte size, backend type). */
|
|
481
|
+
getInfo(): Promise<StorageInfo>;
|
|
482
|
+
/** Get browser storage quota estimate. */
|
|
483
|
+
getQuota(): Promise<StorageQuota>;
|
|
484
|
+
/** Delete all saves for the current game. */
|
|
485
|
+
clearGameData(): Promise<void>;
|
|
486
|
+
/** Delete all Spindle data across all games. */
|
|
487
|
+
clearAllData(): Promise<void>;
|
|
488
|
+
/** Delete a specific playthrough and its saves. */
|
|
489
|
+
deletePlaythrough(playthroughId: string): Promise<void>;
|
|
490
|
+
/** The active storage backend. */
|
|
491
|
+
readonly backend: 'indexeddb' | 'localstorage' | 'memory';
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
/** Return all registered interactive actions. */
|
|
495
|
+
getActions(): StoryAction[];
|
|
496
|
+
|
|
497
|
+
/** Perform a registered action by ID. */
|
|
498
|
+
performAction(id: string, value?: unknown): void;
|
|
499
|
+
|
|
500
|
+
/** Subscribe to a story event. Returns an unsubscribe function. */
|
|
501
|
+
on<E extends StoryEvent>(
|
|
502
|
+
event: E,
|
|
503
|
+
callback: StoryEventCallback<E>,
|
|
504
|
+
): () => void;
|
|
505
|
+
|
|
506
|
+
/** Wait for the next frame's actions to be registered, then return them. */
|
|
507
|
+
waitForActions(): Promise<StoryAction[]>;
|
|
508
|
+
|
|
509
|
+
/** Register a trigger that fires when a condition expression becomes truthy. Returns an unsubscribe function. */
|
|
510
|
+
watch(
|
|
511
|
+
condition: string,
|
|
512
|
+
callbackOrOptions: (() => void) | WatchOptions,
|
|
513
|
+
): () => void;
|
|
514
|
+
|
|
515
|
+
/** Remove a named trigger registered with `watch()`. */
|
|
516
|
+
unwatch(name: string): void;
|
|
517
|
+
|
|
518
|
+
/** Enable or disable the `{nobr}` (no line breaks) rendering mode globally. */
|
|
519
|
+
setNobr(enabled: boolean): void;
|
|
520
|
+
|
|
521
|
+
/** Enable or disable the story stylesheet. */
|
|
522
|
+
setCSS(enabled: boolean): void;
|
|
523
|
+
|
|
524
|
+
/** Set the default passage transition. Pass `null` to clear. */
|
|
525
|
+
setTransition(config: TransitionConfig | null): void;
|
|
526
|
+
|
|
527
|
+
/** Set a one-time transition for the next navigation only. Pass `null` to clear. */
|
|
528
|
+
setNextTransition(config: TransitionConfig | null): void;
|
|
529
|
+
|
|
530
|
+
/** Defer initial passage rendering until `ready()` is called. */
|
|
531
|
+
deferRender(): void;
|
|
532
|
+
|
|
533
|
+
/** Unblock deferred rendering (call after `deferRender()`). */
|
|
534
|
+
ready(): void;
|
|
535
|
+
|
|
536
|
+
/** Return a random float in [0, 1). Uses the seeded PRNG if enabled, otherwise Math.random(). */
|
|
537
|
+
random(): number;
|
|
538
|
+
|
|
539
|
+
/** Return a random integer in [min, max] (inclusive). */
|
|
540
|
+
randomInt(min: number, max: number): number;
|
|
541
|
+
|
|
542
|
+
/** Story configuration. */
|
|
543
|
+
readonly config: {
|
|
544
|
+
/** Maximum number of history moments to retain. */
|
|
545
|
+
maxHistory: number;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
/** Seedable pseudo-random number generator. */
|
|
549
|
+
readonly prng: {
|
|
550
|
+
/** Initialize the PRNG with an optional seed. */
|
|
551
|
+
init(seed?: string, useEntropy?: boolean): void;
|
|
552
|
+
/** Check whether the seeded PRNG is active. */
|
|
553
|
+
isEnabled(): boolean;
|
|
554
|
+
/** The current PRNG seed. */
|
|
555
|
+
readonly seed: string;
|
|
556
|
+
/** The number of values pulled from the current seed. */
|
|
557
|
+
readonly pull: number;
|
|
558
|
+
};
|
|
231
559
|
}
|